ref 功能开始移植

This commit is contained in:
DaxPay
2024-07-08 19:13:14 +08:00
parent 70a585b1de
commit 5b486170be
24 changed files with 1289 additions and 76 deletions

View File

@@ -121,7 +121,6 @@
"vite-plugin-vue-devtools": "^7.3.5", "vite-plugin-vue-devtools": "^7.3.5",
"vue-tsc": "^2.0.24" "vue-tsc": "^2.0.24"
}, },
"packageManager": "pnpm@9.0.4",
"engines": { "engines": {
"node": ">=18.12.0", "node": ">=18.12.0",
"pnpm": ">=9.0.2" "pnpm": ">=9.0.2"

View File

@@ -1,25 +0,0 @@
import { defHttp } from '@/utils/http/axios'
import { getMenuListResultModel, MenuAndResource } from './model/menuModel'
import { Result } from '#/axios'
enum Api {
GetMenuList = '/getMenuList',
}
/**
* @description: Get user menu based on id
*/
export const getMenuList = () => {
return defHttp.get<getMenuListResultModel>({ url: Api.GetMenuList })
}
/**
* 获取菜单和权限码
*/
export const getPermissions = (clientCode: string) => {
return defHttp.get<Result<MenuAndResource>>({
url: '/role/menu/getPermissions',
params: { clientCode },
})
}

View File

@@ -1,31 +1,3 @@
import type { RouteMeta } from 'vue-router'
export interface RouteItem {
path: string
component: any
meta: RouteMeta
name?: string
alias?: string | string[]
redirect?: string
caseSensitive?: boolean
children?: RouteItem[]
}
/**
* @description: Get menu return value
*/
export type getMenuListResultModel = RouteItem[]
/**
* 用户菜单及资源权限返回类
*/
export interface MenuAndResource {
// 权限码
resourcePerms: Array<string>
// 菜单
menus: Array<PermMenu>
}
/** /**
* 权限菜单 * 权限菜单
*/ */

22
src/api/sys/permission.ts Normal file
View File

@@ -0,0 +1,22 @@
import { defHttp } from '@/utils/http/axios'
import { PermMenu } from './model/menuModel'
import { Result } from '#/axios'
/**
* 获取菜单
*/
export const menuTree = (clientCode: string) => {
return defHttp.get<Result<PermMenu[]>>({
url: '/perm/menu/tree',
params: { clientCode },
})
}
/**
* 获取权限码列表
*/
export function getPermCodes() {
return defHttp.get<Result<string[]>>({
url: '/perm/code/findCodesByUser',
})
}

View File

@@ -0,0 +1,9 @@
/**
* 表单编辑类型
*/
export enum FormEditType {
Add,
Edit,
Show,
Other,
}

View File

@@ -3,10 +3,8 @@
*/ */
export enum ResultEnum { export enum ResultEnum {
SUCCESS = 0, SUCCESS = 0,
ERROR = -1,
TIMEOUT = 401, TIMEOUT = 401,
NOT_LOGIN = 10004, NOT_LOGIN = 10401,
TYPE = 'success',
} }
/** /**

View File

@@ -0,0 +1,66 @@
import { useDictStore } from '@/store/modules/dict'
import { LabeledValue } from 'ant-design-vue/lib/select'
import { Dict } from '#/store'
const dictStore = useDictStore()
/**
* 获取字典列表
*/
async function getDict(): Promise<Dict[]> {
const dictList = dictStore.getDict
if (dictList.length > 0) {
return dictList
} else {
return await dictStore.initDict()
}
}
/**
* 字典项转换
*/
function dictConvert(dictCode: string, code) {
const dictList = dictStore.getDict
const item = dictList.filter((dict) => {
return dictCode === dict.dictCode && dict.code === String(code)
})
if (item && item.length > 0) {
return item[0].name
} else {
return ''
}
}
/**
* 获取字典下拉框数据列表
*/
async function dictDropDown(dictCode: string): Promise<LabeledValue[]> {
const list = (await getDict()) || []
return list
.filter((dict) => dictCode === dict.dictCode)
.map((o) => {
return { label: o.name, value: o.code }
})
}
/**
* 获取字典下拉框数据列表(value值为数字)
*/
async function dictDropDownNumber(dictCode: string): Promise<LabeledValue[]> {
const list = (await getDict()) || []
return list
.filter((dict) => dictCode === dict.dictCode)
.map((o) => {
return { label: o.name, value: Number(o.code) }
})
}
/**
* 字典hooks
*/
export function useDict() {
return {
dictConvert,
dictDropDown,
dictDropDownNumber,
}
}

View File

@@ -0,0 +1,104 @@
import { reactive, toRefs } from 'vue'
import { FormEditType } from '@/enums/formTypeEnum'
export default function () {
const model = reactive({
// 表单项标题栅格宽度
labelCol: {
sm: { span: 7 },
},
// 表单项内容栅格宽度
wrapperCol: {
sm: { span: 13 },
},
title: '新增',
modalWidth: 640,
confirmLoading: false,
visible: false,
editable: false,
addable: false,
showable: false,
formEditType: FormEditType.Add,
})
/**
* 状态
*/
const {
labelCol,
wrapperCol,
title,
modalWidth,
confirmLoading,
visible,
editable,
addable,
showable,
formEditType,
} = toRefs(model)
/**
* 初始化表单状态
*/
function initFormEditType(editType: FormEditType) {
formEditType.value = editType
visible.value = true
if (formEditType.value === FormEditType.Add) {
addable.value = true
title.value = '新增'
}
if (formEditType.value === FormEditType.Edit) {
editable.value = true
title.value = '修改'
}
if (formEditType.value === FormEditType.Show) {
showable.value = true
title.value = '查看'
}
}
/**
* 关闭
*/
function handleCancel() {
visible.value = false
addable.value = false
editable.value = false
showable.value = false
}
/**
* 搜索供select下拉框组件进行筛选时使用(:filter-option="search")
*/
function search(input: string, option) {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
/**
* 判断脱敏参数是否被修改的参数, 未修改返回空值 rawForm 后端获取到的原始数据, editForm 修改后的数据, keys 字段名称
*/
function diffForm(rawForm, editForm, ...keys) {
const form = {}
for (const key of keys) {
form[key] = rawForm[key] === editForm[key] ? undefined : editForm[key]
}
return form
}
return {
model,
labelCol,
wrapperCol,
title,
modalWidth,
confirmLoading,
visible,
editable,
addable,
showable,
formEditType,
initFormEditType,
handleCancel,
search,
diffForm,
}
}

View File

@@ -0,0 +1,112 @@
import { TablePageModel } from '#/web'
import { reactive, ref } from 'vue'
import { PageResult } from '#/axios'
/**
* 获取数据对象
*/
export default function <T>(queryPageCallback: CallableFunction) {
// 数据内容
const model = reactive({
pages: {
size: 10,
current: 1,
},
queryParam: {},
pagination: {},
} as TablePageModel<T>)
// 加载状态
const loading = ref(false)
// 批量操作标识
const batchOperateFlag = ref(false)
// 超级查询条件生效状态
const superQueryFlag = ref(false)
// 排序条件
const sortParam = reactive({
sortField: null,
asc: false,
})
// 不可以被重新赋值, 否则会失去绑定
const { pages, pagination } = model
/**
* 普通查询
*/
function query() {
superQueryFlag.value = false
batchOperateFlag.value = false
resetPage()
queryPageCallback()
}
/**
* 表格翻页或变动
*/
function handleTableChange({ currentPage, pageSize }) {
batchOperateFlag.value = false
pages.current = currentPage
pages.size = pageSize
queryPageCallback()
}
/**
* 排序条件变动
*/
function sortChange({ order, property }) {
sortParam.sortField = order ? property : null
sortParam.asc = order === 'asc'
queryPageCallback()
}
/**
* 重置当前页数
*/
function resetPage() {
pages.current = 1
}
/**
* 分页查询返回结果处理
*/
function pageQueryResHandel(res: PageResult) {
pagination.current = Number(res.current)
pagination.size = Number(res.size)
pagination.total = Number(res.total)
pagination.records = res.records
loading.value = false
}
/**
* 重置查询
*/
function resetQuery() {
resetQueryParams()
queryPageCallback()
}
/**
* 重置查询参数
*/
function resetQueryParams() {
superQueryFlag.value = false
model.queryParam = {}
}
/**
* ok按钮
*/
function handleOk() {
queryPageCallback()
}
return {
model,
loading,
pages,
pagination,
batchOperateFlag,
superQueryFlag,
sortParam,
query,
sortChange,
resetPage,
pageQueryResHandel,
handleTableChange,
resetQuery,
resetQueryParams,
handleOk,
}
}

View File

@@ -0,0 +1,28 @@
import { useUserStoreWithOut } from '@/store/modules/user'
import { computed } from 'vue'
import { getAppEnvConfig } from '@/utils/env'
const useUserStore = useUserStoreWithOut()
const { VITE_GLOB_API_URL, VITE_GLOB_API_URL_PREFIX } = getAppEnvConfig()
export function useUpload(uploadUrl: string) {
/**
* 从 localstorage 获取 token
*/
const tokenHeader = computed(() => {
const token = useUserStore.getToken
return {
AccessToken: token,
}
})
/**
* 上传地址
*/
const uploadAction = computed(() => {
return VITE_GLOB_API_URL + VITE_GLOB_API_URL_PREFIX + uploadUrl
})
return {
tokenHeader,
uploadAction,
}
}

View File

@@ -0,0 +1,35 @@
import { FormEditType } from '@/enums/formTypeEnum'
import { unref } from 'vue'
/**
* 服务器校验
* @param value 要进行查重的值
* @param id 主键
* @param formEditType 方法类型,新增或更新
* @param existsFun 查询该值的记录是否存在的请求方法
* @param existsNotIdFun 查询该值id对应的数据之外否存在记录是的请求方法
* @param errMsg 验证不通过的内容
*/
async function existsByServer(
value,
id,
formEditType,
existsFun,
existsNotIdFun,
errMsg = '该编码已存在!',
) {
if (!value) {
return Promise.resolve()
}
const res =
unref(formEditType) === FormEditType.Edit
? await existsNotIdFun(value, id)
: await existsFun(value)
return res.data ? Promise.reject(errMsg) : Promise.resolve()
}
export function useValidate() {
return {
existsByServer,
}
}

View File

@@ -19,7 +19,7 @@ const Dashboard: AppRouteModule = {
component: () => import('@/views/dashboard/analysis/index.vue'), component: () => import('@/views/dashboard/analysis/index.vue'),
meta: { meta: {
// affix: true, // affix: true,
title: '分析页', title: '页',
}, },
}, },
], ],

View File

@@ -45,7 +45,25 @@ export const OUTSIDE: AppRouteModule = {
], ],
} }
/**
* TODO 临时功能开发
*/
export const TEMP: AppRouteModule = {
path: '/temp',
name: 'PROJECT_TEMP',
component: LAYOUT,
meta: { title: '临时功能' },
children: [
{
path: 'dict',
name: 'Dict',
component: () => import('@/views/baseapi/dict/DictList.vue'),
meta: { title: '字典' },
},
],
}
/** /**
* 项目基础路由 * 项目基础路由
*/ */
export const PROJECT_BASE = [INTERNAL, OUTSIDE] export const PROJECT_BASE = [INTERNAL, OUTSIDE, TEMP]

44
src/store/modules/dict.ts Normal file
View File

@@ -0,0 +1,44 @@
import { defineStore } from 'pinia'
import { findAllByEnable } from '@/views/baseapi/dict/DictItem.api'
import { Dict } from '#/store'
import { store } from '@/store'
interface DictState {
dict: Dict[]
}
export const useDictStore = defineStore({
id: 'app-dict',
state: (): DictState => ({
dict: [],
}),
getters: {
getDict(): Dict[] {
// 获取字典
return this.dict
},
},
actions: {
/**
* 初始化字典, 并返回字典列表
*/
async initDict() {
const { data } = await findAllByEnable()
this.dict = data.map((o) => {
return {
dictCode: o.dictCode,
code: o.code,
name: o.name,
} as Dict
})
console.log('初始化字典')
return this.dict
},
},
})
// Need to be used outside the setup
export function useDictStoreWithOut() {
return useDictStore(store)
}
// 初始化字典
useDictStoreWithOut().initDict().then()

View File

@@ -9,7 +9,7 @@ import { PAGE_NOT_FOUND_ROUTE } from '@/router/routes/basic'
import { filter } from '@/utils/helper/treeHelper' import { filter } from '@/utils/helper/treeHelper'
import { getPermissions } from '@/api/sys/menu' import { getPermCodes, menuTree } from '@/api/sys/permission'
import { useMessage } from '@/hooks/web/useMessage' import { useMessage } from '@/hooks/web/useMessage'
import { PageEnum } from '@/enums/pageEnum' import { PageEnum } from '@/enums/pageEnum'
@@ -82,13 +82,18 @@ export const usePermissionStore = defineStore({
*/ */
async changeMenuAndPermCode(): Promise<PermMenu[]> { async changeMenuAndPermCode(): Promise<PermMenu[]> {
const { VITE_GLOB_APP_CLIENT } = getAppEnvConfig() const { VITE_GLOB_APP_CLIENT } = getAppEnvConfig()
const { // 菜单
data: { menus, resourcePerms }, const { data: menus } = await menuTree(VITE_GLOB_APP_CLIENT)
} = await getPermissions(VITE_GLOB_APP_CLIENT as string) // 权限码
this.setPermCodeList(resourcePerms) const { data: permCode } = await getPermCodes()
this.setPermCodeList(permCode)
return menus return menus
}, },
/**
* 获取菜单
*/
/** /**
* 转换路由为系统中的菜单 * 转换路由为系统中的菜单
*/ */
@@ -131,7 +136,7 @@ export const usePermissionStore = defineStore({
} }
/** /**
* @description 根据设置的首页path修正routes中的affix标记固定首页 * 根据设置的首页path修正routes中的affix标记固定首页
* */ * */
const patchHomeAffix = (routes: AppRouteRecordRaw[]) => { const patchHomeAffix = (routes: AppRouteRecordRaw[]) => {
if (!routes || routes.length === 0) return if (!routes || routes.length === 0) return

View File

@@ -0,0 +1,94 @@
import { defHttp } from '@/utils/http/axios'
import { PageResult, Result } from '#/axios'
import { BaseEntity } from '#/web'
/**
* 分页
*/
export const page = (params) => {
return defHttp.get<Result<PageResult<Dict>>>({
url: '/dict/page',
params,
})
}
/**
* 获取单条
*/
export const get = (id) => {
return defHttp.get<Result<Dict>>({
url: '/dict/findById',
params: { id },
})
}
/**
* 添加
*/
export const add = (obj: Dict) => {
return defHttp.post({
url: '/dict/add',
data: obj,
})
}
/**
* 更新
*/
export const update = (obj: Dict) => {
return defHttp.post({
url: '/dict/update',
data: obj,
})
}
/**
* 删除
*/
export const del = (id) => {
return defHttp.delete({
url: '/dict/delete',
params: { id },
})
}
/**
* 查询全部
*/
export const findAll = () => {
return defHttp.get<Result<Array<Dict>>>({
url: '/dict/findAll',
})
}
/**
* 编码是否存在
*/
export function existsByCode(code) {
return defHttp.get<Result<boolean>>({
url: '/dict/existsByCode',
params: { code },
})
}
export function existsByCodeNotId(code, id) {
return defHttp.get<Result<boolean>>({
url: '/dict/existsByCodeNotId',
params: { code, id },
})
}
/**
* 字典
*/
export interface Dict extends BaseEntity {
// 编码
code: string
// 名称
name: string
// 是否启用
enable?: boolean
// 分类标签
groupTag: string
// 备注
remark: string
}

View File

@@ -0,0 +1,156 @@
<template>
<basic-modal
v-bind="$attrs"
:loading="confirmLoading"
:width="modalWidth"
:title="title"
:visible="visible"
:mask-closable="showable"
@cancel="handleCancel"
>
<a-form
class="small-from-item"
ref="formRef"
:model="form"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="主键" name="id" :hidden="true">
<a-input v-model:value="form.id" :disabled="showable" />
</a-form-item>
<a-form-item label="编码" name="code">
<a-input v-model:value="form.code" :disabled="showable" placeholder="请输入编码" />
</a-form-item>
<a-form-item label="名称" name="name">
<a-input v-model:value="form.name" :disabled="showable" placeholder="请输入名称" />
</a-form-item>
<a-form-item label="是否启用" name="enable">
<a-switch
checked-children="启用"
un-checked-children="停用"
v-model:checked="form.enable"
:disabled="showable"
/>
</a-form-item>
<a-form-item label="分类标签" name="groupTag">
<a-input v-model:value="form.groupTag" :disabled="showable" placeholder="请输入分类标签" />
</a-form-item>
<a-form-item label="备注" name="remark">
<a-textarea
:rows="3"
v-model:value="form.remark"
:disabled="showable"
placeholder="请输入备注"
/>
</a-form-item>
</a-form>
<template #footer>
<a-space>
<a-button key="cancel" @click="handleCancel">取消</a-button>
<a-button
v-if="!showable"
key="forward"
:loading="confirmLoading"
type="primary"
@click="handleOk"
>保存</a-button
>
</a-space>
</template>
</basic-modal>
</template>
<script lang="ts" setup>
import { nextTick, reactive, ref } from 'vue'
import useFormEdit from '@/hooks/bootx/useFormEdit'
import { add, get, update, existsByCode, existsByCodeNotId, Dict } from './Dict.api'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { FormEditType } from '@/enums/formTypeEnum'
import { BasicModal } from '@/components/Modal'
import { useValidate } from '@/hooks/bootx/useValidate'
const {
initFormEditType,
handleCancel,
labelCol,
wrapperCol,
modalWidth,
title,
confirmLoading,
visible,
showable,
formEditType,
} = useFormEdit()
const { existsByServer } = useValidate()
// 表单
const formRef = ref<FormInstance>()
let form = reactive({
id: null,
code: '',
name: '',
enable: true,
groupTag: '',
remark: '',
} as Dict)
// 校验
const rules = reactive({
code: [
{ required: true, message: '请输入字典编码', trigger: ['blur', 'change'] },
{ validator: validateCode, trigger: 'blur' },
],
name: [{ required: true, message: '请输入字典名称', trigger: ['blur', 'change'] }],
} as Record<string, Rule[]>)
// 事件
const emits = defineEmits(['ok'])
// 入口
function init(id, editType: FormEditType) {
initFormEditType(editType)
resetForm()
getInfo(id, editType)
}
// 获取信息
function getInfo(id, editType: FormEditType) {
if ([FormEditType.Edit, FormEditType.Show].includes(editType)) {
confirmLoading.value = true
get(id).then(({ data }) => {
form = data
confirmLoading.value = false
})
} else {
confirmLoading.value = false
}
}
// 保存
function handleOk() {
formRef.value?.validate().then(async () => {
confirmLoading.value = true
if (formEditType.value === FormEditType.Add) {
await add(form)
} else if (formEditType.value === FormEditType.Edit) {
await update(form)
}
confirmLoading.value = false
handleCancel()
emits('ok')
})
}
// 重置表单的校验
function resetForm() {
nextTick(() => {
formRef.value?.resetFields()
})
}
// 校验编码重复
async function validateCode() {
const { code, id } = form
return existsByServer(code, id, formEditType, existsByCode, existsByCodeNotId)
}
defineExpose({
init,
})
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,106 @@
import { defHttp } from '@/utils/http/axios'
import { PageResult, Result } from '#/axios'
import { BaseEntity } from '#/web'
/**
* 分页
*/
export const page = (params) => {
return defHttp.get<Result<PageResult<DictItem>>>({
url: '/dict/item/pageByDictionaryId',
params,
})
}
/**
* 获取单条
*/
export const get = (id) => {
return defHttp.get<Result<DictItem>>({
url: '/dict/item/findById',
params: { id },
})
}
/**
* 添加
*/
export const add = (obj: DictItem) => {
return defHttp.post({
url: '/dict/item/add',
data: obj,
})
}
/**
* 更新
*/
export const update = (obj: DictItem) => {
return defHttp.post({
url: '/dict/item/update',
data: obj,
})
}
/**
* 删除
*/
export const del = (id) => {
return defHttp.delete({
url: '/dict/item/delete',
params: { id },
})
}
/**
* 查询全部
*/
export const findAll = () => {
return defHttp.get<Result<Array<DictItem>>>({
url: '/dict/item/findAll',
})
}
/**
* 查询全部已经启用
*/
export const findAllByEnable = () => {
return defHttp.get<Result<Array<DictItem>>>({
url: '/dict/item/findAllByEnable',
})
}
/**
* 编码是否存在
*/
export function existsByCode(code) {
return defHttp.get<Result<boolean>>({
url: '/dict/item/existsByCode',
params: { code },
})
}
export function existsByCodeNotId(code, id) {
return defHttp.get<Result<boolean>>({
url: '/dict/item/existsByCodeNotId',
params: { code, id },
})
}
/**
* 字典项
*/
export interface DictItem extends BaseEntity {
// 字典id
dictId?: number | null
// 字典code
dictCode?: string | null
// 字典项code
code?: string
// 字典项名称
name?: string
// 是否启用
enable?: boolean
// 排序
sortNo?: number
// 备注
remark?: string
}

View File

@@ -0,0 +1,159 @@
<template>
<basic-modal
v-bind="$attrs"
:loading="confirmLoading"
:width="modalWidth"
:title="title"
:visible="visible"
:mask-closable="showable"
@cancel="handleCancel"
>
<a-form
class="small-from-item"
ref="formRef"
:model="form"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="主键" name="id" :hidden="true">
<a-input v-model:value="form.id" :disabled="showable" />
</a-form-item>
<a-form-item label="字典编码" name="dictCode">
<a-input v-model:value="form.dictCode" disabled />
</a-form-item>
<a-form-item label="字典项编码" name="code">
<a-input v-model:value="form.code" :disabled="showable" placeholder="请输入字典项code" />
</a-form-item>
<a-form-item label="字典项名称" name="name">
<a-input v-model:value="form.name" :disabled="showable" placeholder="请输入字典项名称" />
</a-form-item>
<a-form-item label="排序" name="sortNo">
<a-input-number v-model:value="form.sortNo" :disabled="showable" placeholder="请输入排序" />
</a-form-item>
<a-form-item label="是否启用" name="enable">
<a-switch
checked-children="启用"
un-checked-children="停用"
v-model:checked="form.enable"
:disabled="showable"
/>
</a-form-item>
<a-form-item label="备注" name="remark">
<a-textarea v-model:value="form.remark" :disabled="showable" placeholder="请输入备注" />
</a-form-item>
</a-form>
<template #footer>
<a-space>
<a-button key="cancel" @click="handleCancel">取消</a-button>
<a-button
v-if="!showable"
key="forward"
:loading="confirmLoading"
type="primary"
@click="handleOk"
>保存</a-button
>
</a-space>
</template>
</basic-modal>
</template>
<script lang="ts" setup>
import { nextTick, reactive, ref } from 'vue'
import useFormEdit from '@/hooks/bootx/useFormEdit'
import { add, get, update, existsByCode, existsByCodeNotId, DictItem } from './DictItem.api'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { FormEditType } from '@/enums/formTypeEnum'
import { BasicModal } from '@/components/Modal'
import { Dict } from './Dict.api'
import { useValidate } from '@/hooks/bootx/useValidate'
const {
initFormEditType,
handleCancel,
labelCol,
wrapperCol,
modalWidth,
title,
confirmLoading,
visible,
showable,
formEditType,
} = useFormEdit()
const { existsByServer } = useValidate()
// 表单
const formRef = ref<FormInstance>()
let form = reactive({
id: null,
dictId: null,
dictCode: null,
code: '',
name: '',
enable: true,
sortNo: 0,
remark: '',
} as DictItem)
// 校验
const rules = reactive({
code: [
{ required: true, message: '请输入字典项编码', trigger: ['blur', 'change'] },
{ validator: validateCode, trigger: 'blur' },
],
name: [{ required: true, message: '请输入字典项名称', trigger: ['blur', 'change'] }],
} as Record<string, Rule[]>)
// 事件
const emits = defineEmits(['ok'])
// 入口
function init(id, editType: FormEditType, dict: Dict) {
initFormEditType(editType)
resetForm()
form.dictId = dict.id as number
form.dictCode = dict.code
getInfo(id, editType)
}
// 获取信息
function getInfo(id, editType: FormEditType) {
if ([FormEditType.Edit, FormEditType.Show].includes(editType)) {
confirmLoading.value = true
get(id).then(({ data }) => {
form = reactive(data)
confirmLoading.value = false
})
} else {
confirmLoading.value = false
}
}
// 保存
function handleOk() {
formRef.value?.validate().then(async () => {
confirmLoading.value = true
if (formEditType.value === FormEditType.Add) {
await add(form)
} else if (formEditType.value === FormEditType.Edit) {
await update(form)
}
confirmLoading.value = false
handleCancel()
emits('ok')
})
}
// 校验编码重复
async function validateCode() {
const { code, id } = form
return existsByServer(code, id, formEditType, existsByCode, existsByCodeNotId)
}
// 重置表单的校验
function resetForm() {
nextTick(() => {
formRef?.resetFields()
})
}
defineExpose({
init,
})
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,128 @@
<template>
<basic-drawer
forceRender
v-bind="$attrs"
title="字典列表"
width="60%"
:visible="visible"
@close="visible = false"
>
<vxe-toolbar ref="xToolbar" custom :refresh="{ queryMethod: queryPage }">
<template #buttons>
<a-space>
<a-button type="primary" pre-icon="ant-design:plus-outlined" @click="add">新建</a-button>
</a-space>
</template>
</vxe-toolbar>
<vxe-table row-id="id" ref="xTable" :data="pagination.records" :loading="loading">
<vxe-column type="seq" width="60" />
<vxe-column field="code" title="字典项编码" />
<vxe-column field="name" title="字典项名称" />
<vxe-column field="enable" title="启用状态">
<template #default="{ row }">
<a-tag v-if="row.enable" color="green">启用</a-tag>
<a-tag v-else color="red">停用</a-tag>
</template>
</vxe-column>
<vxe-column field="sortNo" title="排序" />
<vxe-column field="remark" title="备注" />
<vxe-column field="createTime" title="创建时间" />
<vxe-column fixed="right" width="150" :showOverflow="false" title="操作">
<template #default="{ row }">
<span>
<a href="javascript:" @click="show(row)">查看</a>
</span>
<a-divider type="vertical" />
<span>
<a href="javascript:" @click="edit(row)">编辑</a>
</span>
<a-divider type="vertical" />
<a-popconfirm title="是否删除" @confirm="remove(row)" okText="是" cancelText="否">
<a href="javascript:" style="color: red">删除</a>
</a-popconfirm>
</template>
</vxe-column>
</vxe-table>
<vxe-pager
size="medium"
:loading="loading"
:current-page="pagination.current"
:page-size="pagination.size"
:total="pagination.total"
@page-change="handleTableChange"
/>
<dict-item-edit ref="dictItemEdit" @ok="queryPage" />
</basic-drawer>
</template>
<script lang="ts" setup>
import { nextTick, ref } from 'vue'
import { del, page } from './DictItem.api'
import useTablePage from '@/hooks/bootx/useTablePage'
import DictItemEdit from './DictItemEdit.vue'
import { VxeTableInstance, VxeToolbarInstance } from 'vxe-table'
import { FormEditType } from '@/enums/formTypeEnum'
import { useMessage } from '@/hooks/web/useMessage'
import { Dict } from '@/views/baseapi/dict/Dict.api'
import BasicDrawer from '@/components/Drawer/src/BasicDrawer.vue'
// 使用hooks
const { handleTableChange, pageQueryResHandel, pagination, pages, model, loading } =
useTablePage(queryPage)
const { createMessage } = useMessage()
// 查询条件
let visible = ref(false)
let dictInfo = ref<Dict>()
const xTable = ref<VxeTableInstance>()
const xToolbar = ref<VxeToolbarInstance>()
const dictItemEdit = ref<any>()
nextTick(() => {
xTable.value?.connect(xToolbar.value as VxeToolbarInstance)
})
function init(dict) {
visible.value = true
dictInfo.value = dict
queryPage()
}
// 分页查询
function queryPage() {
loading.value = true
page({
...model.queryParam,
...pages,
dictId: dictInfo.value?.id,
}).then(({ data }) => {
pageQueryResHandel(data)
})
}
// 新增
function add() {
dictItemEdit.value.init(null, FormEditType.Add, dictInfo)
}
// 查看
function edit(record) {
dictItemEdit.value.init(record.id, FormEditType.Edit, dictInfo)
}
// 查看
function show(record) {
dictItemEdit.value.init(record.id, FormEditType.Show, dictInfo)
}
// 删除
function remove(record) {
del(record.id).then(() => {
createMessage.success('删除成功')
queryPage()
})
}
defineExpose({
init,
})
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,153 @@
<template>
<div>
<div class="m-3 p-3 pt-5 bg-white">
<b-query
:query-params="model.queryParam"
:fields="fields"
@query="queryPage"
@reset="resetQueryParams"
/>
</div>
<div class="m-3 p-3 bg-white">
<vxe-toolbar ref="xToolbar" custom :refresh="{ queryMethod: queryPage }">
<template #buttons>
<a-space>
<a-button type="primary" pre-icon="ant-design:plus-outlined" @click="add"
>新建</a-button
>
</a-space>
</template>
</vxe-toolbar>
<vxe-table row-id="id" ref="xTable" :data="pagination.records" :loading="loading">
<vxe-column type="seq" width="60" />
<vxe-column field="code" title="编码" />
<vxe-column field="name" title="名称" />
<vxe-column field="groupTag" title="分类标签">
<template #default="{ row }">
<a-tag color="green">{{ row.groupTag || '空' }}</a-tag>
</template>
</vxe-column>
<vxe-column field="enable" title="启用状态">
<template #default="{ row }">
<a-tag v-if="row.enable" color="green">启用</a-tag>
<a-tag v-else color="red">停用</a-tag>
</template>
</vxe-column>
<vxe-column field="remark" title="备注" />
<vxe-column field="createTime" title="创建时间" />
<vxe-column fixed="right" width="220" :showOverflow="false" title="操作">
<template #default="{ row }">
<span>
<a href="javascript:" @click="show(row)">查看</a>
</span>
<a-divider type="vertical" />
<span>
<a href="javascript:" @click="edit(row)">编辑</a>
</span>
<a-divider type="vertical" />
<span>
<a href="javascript:" @click="itemList(row)">字典配置</a>
</span>
<a-divider type="vertical" />
<a-popconfirm title="是否删除" @confirm="remove(row)" okText="是" cancelText="否">
<a href="javascript:" style="color: red">删除</a>
</a-popconfirm>
</template>
</vxe-column>
</vxe-table>
<vxe-pager
size="medium"
:loading="loading"
:current-page="pagination.current"
:page-size="pagination.size"
:total="pagination.total"
@page-change="handleTableChange"
/>
<dict-edit ref="dictEdit" @ok="queryPage" />
<dict-item-list ref="dictItemList" />
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import { del, page } from './Dict.api'
import useTablePage from '@/hooks/bootx/useTablePage'
import DictEdit from './DictEdit.vue'
import { VxeTableInstance, VxeToolbarInstance } from 'vxe-table'
import BQuery from '@/components/Bootx/Query/BQuery.vue'
import { FormEditType } from '@/enums/formTypeEnum'
import { useMessage } from '@/hooks/web/useMessage'
import { QueryField, STRING } from '@/components/Bootx/Query/Query'
import DictItemList from './DictItemList.vue'
// 使用hooks
const {
handleTableChange,
pageQueryResHandel,
resetQueryParams,
pagination,
pages,
model,
loading,
} = useTablePage(queryPage)
const { notification, createMessage } = useMessage()
// 查询条件
const fields = [
{ field: 'code', type: STRING, name: '字典编码', placeholder: '请输入字典编码' },
{ field: 'name', type: STRING, name: '字典名称', placeholder: '请输入字典名称' },
{ field: 'groupTag', type: STRING, name: '分组标签', placeholder: '请输入分组标签' },
] as QueryField[]
const xTable = $ref<VxeTableInstance>()
const xToolbar = $ref<VxeToolbarInstance>()
const dictEdit = $ref<any>()
const dictItemList = $ref<any>()
onMounted(() => {
vxeBind()
queryPage()
})
function vxeBind() {
xTable?.connect(xToolbar as VxeToolbarInstance)
}
// 分页查询
function queryPage() {
loading.value = true
page({
...model.queryParam,
...pages,
}).then(({ data }) => {
pageQueryResHandel(data)
})
return Promise.resolve()
}
// 新增
function add() {
dictEdit.init(null, FormEditType.Add)
}
// 查看
function edit(record) {
dictEdit.init(record.id, FormEditType.Edit)
}
// 查看
function show(record) {
dictEdit.init(record.id, FormEditType.Show)
}
// 明细列表查看
function itemList(record) {
dictItemList.init(record)
}
// 删除
function remove(record) {
del(record.id).then(() => {
createMessage.success('删除成功')
queryPage()
})
}
</script>
<style lang="less" scoped></style>

View File

@@ -85,7 +85,6 @@
import { LoginStateEnum, useLoginState } from '@/views/sys/login/useLogin' import { LoginStateEnum, useLoginState } from '@/views/sys/login/useLogin'
import { FormInstance } from 'ant-design-vue/lib/form/Form' import { FormInstance } from 'ant-design-vue/lib/form/Form'
import { LoginParams } from '@/api/sys/model/userModel' import { LoginParams } from '@/api/sys/model/userModel'
import { findLoginTypeByCode, LoginType } from '@/api/common/LoginAssist'
import { Rule } from 'ant-design-vue/lib/form' import { Rule } from 'ant-design-vue/lib/form'
import { getAppEnvConfig } from '@/utils/env' import { getAppEnvConfig } from '@/utils/env'
import { imgCaptcha } from '@/api/common/Captcha' import { imgCaptcha } from '@/api/common/Captcha'
@@ -137,14 +136,6 @@
// 终端编码 // 终端编码
const { VITE_GLOB_APP_CLIENT } = getAppEnvConfig() const { VITE_GLOB_APP_CLIENT } = getAppEnvConfig()
form.client = VITE_GLOB_APP_CLIENT form.client = VITE_GLOB_APP_CLIENT
// 获取登录方式
findLoginTypeByCode(form.loginType).then(({ data }) => {
loginType.value = data
if (loginType && loginType.value.captcha && loginType.value.enable) {
console.log(loginType)
getCaptcha()
}
})
} }
/** /**

39
types/web.d.ts vendored Normal file
View File

@@ -0,0 +1,39 @@
import { PageResult } from '#/axios'
/**
* 分页参数
*/
export interface PageParam {
// 每页数量
size: number
// 当前页数
current: number
}
/**
* 分页表格列表对象
*/
export interface TablePageModel<T = any> {
// 分页参数
pages: PageParam
// 查询参数
queryParam: object
// 结果
pagination: PageResult<T>
}
/**
* 基础实体对象
*/
export interface BaseEntity {
id?: number | string | null
createTime?: string | null
}
/**
* 键值对对象
*/
export interface KeyValue {
key: string
value: string
}

View File

@@ -23,7 +23,7 @@ export default defineApplicationConfig({
target: 'http://localhost:9999', target: 'http://localhost:9999',
changeOrigin: true, changeOrigin: true,
ws: true, ws: true,
rewrite: (path) => path.replace(new RegExp(`^/server`), ''), rewrite: (path) => path.replace(new RegExp(`^/admin`), ''),
// only https // only https
// secure: false // secure: false
}, },
@@ -32,7 +32,7 @@ export default defineApplicationConfig({
target: 'http://localhost:8888', target: 'http://localhost:8888',
changeOrigin: true, changeOrigin: true,
ws: true, ws: true,
rewrite: (path) => path.replace(new RegExp(`^/server`), ''), rewrite: (path) => path.replace(new RegExp(`^/merchant`), ''),
// only https // only https
// secure: false // secure: false
}, },