🆕 项目初始化,页面布局待修改完善
5
README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Vue 3 + TypeScript + Vite
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||||
|
|
||||||
|
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
|
43
eslint.config.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import globals from 'globals'
|
||||||
|
import pluginJs from '@eslint/js'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
import pluginVue from 'eslint-plugin-vue'
|
||||||
|
import pluginPrettierRecommendedConfigs from 'eslint-plugin-prettier/recommended'
|
||||||
|
import parserVue from 'vue-eslint-parser'
|
||||||
|
|
||||||
|
import { readFileSync } from 'fs'
|
||||||
|
const fileUrl = new URL('./.eslintrc-auto-import.json', import.meta.url)
|
||||||
|
const esAuto = JSON.parse(readFileSync(fileUrl))
|
||||||
|
|
||||||
|
export default [
|
||||||
|
// eslint 默认推荐规则
|
||||||
|
pluginJs.configs.recommended,
|
||||||
|
// ts 默认推荐规则
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
// vue3 基础推荐规则
|
||||||
|
...pluginVue.configs['flat/recommended'],
|
||||||
|
// prettier 默认推荐规则
|
||||||
|
pluginPrettierRecommendedConfigs,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
...globals.es2020,
|
||||||
|
...globals.node,
|
||||||
|
...esAuto.globals,
|
||||||
|
acl: true,
|
||||||
|
dictionary: true,
|
||||||
|
sensor: true,
|
||||||
|
station: true,
|
||||||
|
},
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
parser: parserVue,
|
||||||
|
parserOptions: {
|
||||||
|
parser: tseslint.parser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: ['public/', 'index.html', 'report.html'],
|
||||||
|
},
|
||||||
|
]
|
14
index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/logo.png" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>库管系统</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
113
package.json
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
{
|
||||||
|
"name": "iot-front",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --host 0.0.0.0 --open",
|
||||||
|
"build": "vue-tsc -b && vite build",
|
||||||
|
"report": "vue-tsc -b && vite build",
|
||||||
|
"commit": "git add . && git-cz && git push",
|
||||||
|
"lint:eslint": "eslint --fix",
|
||||||
|
"lint:format": "prettier --write --log-level warn \"src/**/*.{js,ts,json,tsx,css,less,vue,html,md}\"",
|
||||||
|
"lint:stylelint": "stylelint \"**/*.{css,scss,vue,html}\" --fix",
|
||||||
|
"lint:lint-staged": "lint-staged",
|
||||||
|
"preview": "vite preview --host 0.0.0.0 --open",
|
||||||
|
"prepare": "husky"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
|
"@codemirror/lang-java": "^6.0.1",
|
||||||
|
"@codemirror/lang-javascript": "^6.2.2",
|
||||||
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
|
"@codemirror/lang-markdown": "^6.3.0",
|
||||||
|
"@codemirror/lang-sql": "^6.8.0",
|
||||||
|
"@codemirror/lang-xml": "^6.1.0",
|
||||||
|
"@codemirror/language": "^6.10.3",
|
||||||
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
|
"@kjgl77/datav-vue3": "^1.7.3",
|
||||||
|
"@vueuse/core": "^11.1.0",
|
||||||
|
"@vxe-ui/plugin-export-xlsx": "^4.0.3",
|
||||||
|
"@vxe-ui/plugin-render-antd": "^4.0.7",
|
||||||
|
"ant-design-vue": "^4.2.5",
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"codemirror": "^6.0.1",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"echarts": "^5.5.1",
|
||||||
|
"exceljs": "^4.4.0",
|
||||||
|
"pinia": "^2.2.4",
|
||||||
|
"pinia-plugin-persistedstate": "^4.1.1",
|
||||||
|
"sortablejs": "^1.15.3",
|
||||||
|
"vue": "^3.5.11",
|
||||||
|
"vue-codemirror": "^6.1.1",
|
||||||
|
"vue-echarts": "^7.0.3",
|
||||||
|
"vue-i18n": "^10.0.4",
|
||||||
|
"vue-router": "^4.4.5",
|
||||||
|
"vxe-pc-ui": "^4.2.18",
|
||||||
|
"vxe-table": "^4.7.86",
|
||||||
|
"vxe-table-plugin-antd": "^4.0.8",
|
||||||
|
"xe-utils": "^3.5.31"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "^19.5.0",
|
||||||
|
"@commitlint/config-conventional": "^19.5.0",
|
||||||
|
"@eslint/js": "^9.12.0",
|
||||||
|
"@types/sortablejs": "^1.15.8",
|
||||||
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
|
"@vitejs/plugin-vue-jsx": "^4.0.1",
|
||||||
|
"commitizen": "^4.3.1",
|
||||||
|
"cz-git": "^1.10.1",
|
||||||
|
"eslint": "^9.12.0",
|
||||||
|
"eslint-config-prettier": "^9.1.0",
|
||||||
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
|
"eslint-plugin-vue": "^9.28.0",
|
||||||
|
"globals": "^15.11.0",
|
||||||
|
"husky": "^9.1.6",
|
||||||
|
"lint-staged": "^15.2.10",
|
||||||
|
"pont-engine": "^1.6.3",
|
||||||
|
"prettier": "^3.3.3",
|
||||||
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
|
"stylelint": "^16.9.0",
|
||||||
|
"stylelint-config-html": "^1.1.0",
|
||||||
|
"stylelint-config-prettier": "^9.0.5",
|
||||||
|
"stylelint-config-recess-order": "^5.1.1",
|
||||||
|
"stylelint-config-recommended-scss": "^14.1.0",
|
||||||
|
"stylelint-config-recommended-vue": "^1.5.0",
|
||||||
|
"stylelint-config-standard": "^36.0.1",
|
||||||
|
"typescript": "^5.6.3",
|
||||||
|
"typescript-eslint": "^8.8.1",
|
||||||
|
"unplugin-auto-import": "^0.18.3",
|
||||||
|
"unplugin-vue-components": "^0.27.4",
|
||||||
|
"vite": "^5.4.8",
|
||||||
|
"vite-eslint-plugin": "^1.9.2",
|
||||||
|
"vite-plugin-compression": "^0.5.1",
|
||||||
|
"vite-plugin-vue-devtools": "^7.4.6",
|
||||||
|
"vue-tsc": "^2.1.6"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"commitizen": {
|
||||||
|
"path": "node_modules/cz-git"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,ts}": [
|
||||||
|
"eslint --fix",
|
||||||
|
"prettier --write"
|
||||||
|
],
|
||||||
|
"*.{cjs,json}": [
|
||||||
|
"prettier --write"
|
||||||
|
],
|
||||||
|
"*.{vue,html}": [
|
||||||
|
"eslint --fix",
|
||||||
|
"prettier --write",
|
||||||
|
"stylelint --fix"
|
||||||
|
],
|
||||||
|
"*.{scss,css}": [
|
||||||
|
"stylelint --fix",
|
||||||
|
"prettier --write"
|
||||||
|
],
|
||||||
|
"*.md": [
|
||||||
|
"prettier --write"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
7759
pnpm-lock.yaml
generated
Normal file
34
pont-config.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"outDir": "./src/api",
|
||||||
|
"templatePath": "./pontTemplate",
|
||||||
|
"usingMultipleOrigins": true,
|
||||||
|
"origins": [
|
||||||
|
{
|
||||||
|
"name": "auth",
|
||||||
|
"originUrl": "http://127.0.0.1:7777/v3/api-docs/auth",
|
||||||
|
"originType": "SwaggerV3",
|
||||||
|
"usingOperationId": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "acl",
|
||||||
|
"originUrl": "http://127.0.0.1:7777/v3/api-docs/acl",
|
||||||
|
"originType": "SwaggerV3",
|
||||||
|
"usingOperationId": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dictionary",
|
||||||
|
"originUrl": "http://127.0.0.1:7777/v3/api-docs/dictionary",
|
||||||
|
"originType": "SwaggerV3",
|
||||||
|
"usingOperationId": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "material",
|
||||||
|
"originUrl": "http://127.0.0.1:7777/v3/api-docs/material",
|
||||||
|
"originType": "SwaggerV3",
|
||||||
|
"usingOperationId": true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
537
pontTemplate.ts
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
import * as Pont from 'pont-engine'
|
||||||
|
import { BaseClass, CodeGenerator, Interface } from 'pont-engine'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 首字母大写
|
||||||
|
* @param str 源字符串
|
||||||
|
* @returns 首字母大写字符串
|
||||||
|
*/
|
||||||
|
export function upperFirst(str: string): string {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 数组去重
|
||||||
|
* @param arr 源数组
|
||||||
|
* @returns 去重数组
|
||||||
|
*/
|
||||||
|
export function distinct(arr: Array<unknown>): Array<unknown> {
|
||||||
|
return Array.from(new Set([...arr]))
|
||||||
|
}
|
||||||
|
export const dsUrlMapping = {}
|
||||||
|
|
||||||
|
export function getUrl(ds: string): string {
|
||||||
|
return dsUrlMapping[ds] || ''
|
||||||
|
}
|
||||||
|
export function fixInterfaceName(name: string) {
|
||||||
|
return name.replace(/_\d?/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取路径参数
|
||||||
|
* @param url url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function extractPathParameters(url: string): string[] {
|
||||||
|
const reg = /\{(.+?)\}/
|
||||||
|
const reg_g = /\{(.+?)\}/g
|
||||||
|
const result = url.match(reg_g)
|
||||||
|
if (result == null) {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
const list = result.reduce((pre: string[], item) => {
|
||||||
|
const r = item.match(reg)
|
||||||
|
if (r == null) {
|
||||||
|
return pre
|
||||||
|
} else {
|
||||||
|
pre.push(r[1])
|
||||||
|
return pre
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 求差集
|
||||||
|
* @param arr1
|
||||||
|
* @param arr2
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function subSet(arr1: string[], arr2: string[]) {
|
||||||
|
const set1 = new Set(arr1)
|
||||||
|
const set2 = new Set(arr2)
|
||||||
|
const subset: string[] = []
|
||||||
|
for (const item of set1) {
|
||||||
|
if (!set2.has(item)) {
|
||||||
|
subset.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subset
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FileStructures extends Pont.FileStructures {
|
||||||
|
/**
|
||||||
|
* 多源的文件结构
|
||||||
|
*/
|
||||||
|
getMultipleOriginsFileStructures(): {
|
||||||
|
'index.ts': unknown
|
||||||
|
'api-lock.json': string
|
||||||
|
'api.d.ts': unknown
|
||||||
|
} {
|
||||||
|
const files = {}
|
||||||
|
this.generators.forEach((generator) => {
|
||||||
|
const dsName = generator.dataSource.name
|
||||||
|
const dsFiles = this.getOriginFileStructures(generator, true)
|
||||||
|
files[dsName] = dsFiles
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
...files,
|
||||||
|
'index.ts': this.getDataSourcesTs.bind(this),
|
||||||
|
'api.d.ts': this.getDataSourcesDeclarationTs.bind(this),
|
||||||
|
'api-lock.json': this.getLockContent.bind(this),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* api 插件 index.ts 生成
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getDataSourcesTs(): string {
|
||||||
|
const dsNames = this.generators.map((ge) => ge.dataSource.name)
|
||||||
|
const apis = dsNames
|
||||||
|
.map((name) => {
|
||||||
|
return `${name}Api`
|
||||||
|
})
|
||||||
|
.join(',')
|
||||||
|
return `
|
||||||
|
import type { App } from 'vue';
|
||||||
|
${dsNames
|
||||||
|
.map((name) => {
|
||||||
|
return `import ${name}Api from './${name}/mods';
|
||||||
|
`
|
||||||
|
})
|
||||||
|
.join('\n')}
|
||||||
|
export const api = {
|
||||||
|
${apis},
|
||||||
|
install: (app: App) => {
|
||||||
|
app.config.globalProperties.$api = api;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default api;
|
||||||
|
`
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 生成全局类型定义
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getDataSourcesDeclarationTs(): string {
|
||||||
|
const dsNames = this.generators.map((ge) => ge.dataSource.name)
|
||||||
|
return `
|
||||||
|
/**
|
||||||
|
* mybatis-plus 分页对象
|
||||||
|
*/
|
||||||
|
export interface IPage<T> {
|
||||||
|
/**
|
||||||
|
* 当前页面
|
||||||
|
*/
|
||||||
|
current: number;
|
||||||
|
/**
|
||||||
|
* 总页数
|
||||||
|
*/
|
||||||
|
pages?: number;
|
||||||
|
/**
|
||||||
|
* 数据记录
|
||||||
|
*/
|
||||||
|
records?: Array<T>;
|
||||||
|
/**
|
||||||
|
* 分页大小
|
||||||
|
*/
|
||||||
|
size: number;
|
||||||
|
/**
|
||||||
|
* 数据总条数
|
||||||
|
*/
|
||||||
|
total?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Nutz 分页对象
|
||||||
|
*/
|
||||||
|
export interface Pagination<T> {
|
||||||
|
/**
|
||||||
|
* 数据记录
|
||||||
|
*/
|
||||||
|
dataList: Array<T>;
|
||||||
|
/**
|
||||||
|
* 是否首页
|
||||||
|
*/
|
||||||
|
first?: boolean;
|
||||||
|
/**
|
||||||
|
* 是否尾页
|
||||||
|
*/
|
||||||
|
last?: boolean;
|
||||||
|
/**
|
||||||
|
* 偏移量
|
||||||
|
*/
|
||||||
|
offset?: number;
|
||||||
|
/**
|
||||||
|
* 总页数
|
||||||
|
*/
|
||||||
|
pageCount: number;
|
||||||
|
/**
|
||||||
|
* 当前页码
|
||||||
|
*/
|
||||||
|
pageNumber: number;
|
||||||
|
/**
|
||||||
|
* 页面大小
|
||||||
|
*/
|
||||||
|
pageSize: number;
|
||||||
|
/**
|
||||||
|
* sql参数
|
||||||
|
*/
|
||||||
|
paras: Record<string, unknown>;
|
||||||
|
/**
|
||||||
|
* 数据记录总数
|
||||||
|
*/
|
||||||
|
recordCount?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* VXE 数据结构
|
||||||
|
*/
|
||||||
|
export interface VXETableSaveDTO<T> {
|
||||||
|
/**
|
||||||
|
* 新增记录数据
|
||||||
|
*/
|
||||||
|
insertRecords: Array<T>;
|
||||||
|
/**
|
||||||
|
* 删除记录数据
|
||||||
|
*/
|
||||||
|
removeRecords: Array<T>;
|
||||||
|
/**
|
||||||
|
* 更新记录数据
|
||||||
|
*/
|
||||||
|
updateRecords: Array<T>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 枚举码本
|
||||||
|
*/
|
||||||
|
export interface Codebook {
|
||||||
|
/** 码本编码 */
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
/** 码本名称 */
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
/** 码本值 */
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局错误
|
||||||
|
*/
|
||||||
|
export interface GlobalError {
|
||||||
|
/** 错误码 */
|
||||||
|
code?: number;
|
||||||
|
|
||||||
|
/** 错误信息 */
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* NutMap
|
||||||
|
*/
|
||||||
|
type NutMap<Key extends string, Value = unknown> = {
|
||||||
|
[key in Key]: Value;
|
||||||
|
};
|
||||||
|
// 导入其他模块
|
||||||
|
${dsNames
|
||||||
|
.map((name) => {
|
||||||
|
return `/// <reference path="./${name}/api.d.ts" />`
|
||||||
|
})
|
||||||
|
.join('\n')}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 生成单个数据源文件结构
|
||||||
|
* @param generator 生成器
|
||||||
|
* @param usingMultipleOrigins 是否多源
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getOriginFileStructures(
|
||||||
|
generator: CodeGenerator,
|
||||||
|
usingMultipleOrigins = false,
|
||||||
|
): {
|
||||||
|
[x: string]: unknown
|
||||||
|
mods: Record<string, unknown>
|
||||||
|
'api.d.ts': unknown
|
||||||
|
} {
|
||||||
|
const mods = {}
|
||||||
|
const dataSource = generator.dataSource
|
||||||
|
dataSource.mods.forEach((mod) => {
|
||||||
|
const currMod = {}
|
||||||
|
//每一个接口一个文件
|
||||||
|
mod.interfaces.forEach((inter) => {
|
||||||
|
currMod[fixInterfaceName(inter.name) + '.ts'] = generator.getInterfaceContent.bind(generator, inter)
|
||||||
|
})
|
||||||
|
//接口汇总导出文件和接口声明
|
||||||
|
currMod['index.ts'] = generator.getModIndex.bind(generator, mod)
|
||||||
|
|
||||||
|
//每个模块生成一份
|
||||||
|
mods[mod.name] = currMod
|
||||||
|
|
||||||
|
//每个模块的汇总导出文件和声明文件
|
||||||
|
mods['index.ts'] = generator.getModsIndex.bind(generator)
|
||||||
|
})
|
||||||
|
const result = {
|
||||||
|
mods: mods, //tags
|
||||||
|
'api.d.ts': generator.getDeclaration.bind(generator), // 模块类型声明
|
||||||
|
}
|
||||||
|
if (!usingMultipleOrigins) {
|
||||||
|
result['api-lock.json'] = this.getLockContent.bind(this)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MyGenerator extends CodeGenerator {
|
||||||
|
/**
|
||||||
|
* 生成 namespace 类型定义
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getDeclaration(): string {
|
||||||
|
return `
|
||||||
|
${this.getCommonDeclaration()}
|
||||||
|
${this.getBaseClassesInDeclaration()}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
getBaseClassesInDeclaration(): string {
|
||||||
|
const content = `declare namespace ${this.dataSource.name || 'defs'} {
|
||||||
|
${this.dataSource.baseClasses
|
||||||
|
.filter((item) => {
|
||||||
|
return (
|
||||||
|
item.name !== 'Result' &&
|
||||||
|
!item.name.startsWith('Result') &&
|
||||||
|
item.name !== 'Pagination' &&
|
||||||
|
!item.name.startsWith('Pagination') &&
|
||||||
|
item.name !== 'IPage' &&
|
||||||
|
!item.name.startsWith('IPage') &&
|
||||||
|
item.name !== 'VXETable' &&
|
||||||
|
!item.name.startsWith('VXETable') &&
|
||||||
|
item.name !== 'NutMap' &&
|
||||||
|
!item.name.startsWith('NutMap') &&
|
||||||
|
item.name !== 'Codebook' &&
|
||||||
|
!item.name.startsWith('Codebook') &&
|
||||||
|
item.name !== 'GlobalError' &&
|
||||||
|
!item.name.startsWith('GlobalError')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(
|
||||||
|
(base) => `
|
||||||
|
${this.getBaseClassInDeclaration(base)}
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.join('\n')}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
return content.replace(/defs./g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
getBaseClassInDeclaration(base: BaseClass): string {
|
||||||
|
if (base.templateArgs && base.templateArgs.length) {
|
||||||
|
return `
|
||||||
|
/**
|
||||||
|
* ${base.description || base.name}
|
||||||
|
*/
|
||||||
|
export interface ${base.name}<${base.templateArgs.map((_, index) => `T${index} = any`).join(', ')}> {
|
||||||
|
${base.properties.map((prop) => prop.toPropertyCode(Pont.Surrounding.typeScript, true)).join('\n')}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
/**
|
||||||
|
* ${base.description || base.name}
|
||||||
|
*/
|
||||||
|
export interface ${base.name} {
|
||||||
|
${base.properties.map((prop) => prop.toPropertyCode(Pont.Surrounding.typeScript, true)).join('\n')}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
getParamsTypeDec(inter: Interface): string {
|
||||||
|
if (!inter.parameters.filter((p) => p.in === 'query').length) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return `export interface Params {
|
||||||
|
${inter.parameters
|
||||||
|
.filter((param) => param.in === 'query')
|
||||||
|
.sort((a, b) => {
|
||||||
|
const x = a.required ? 1 : 0
|
||||||
|
const y = b.required ? 1 : 0
|
||||||
|
return y - x
|
||||||
|
})
|
||||||
|
.map(
|
||||||
|
(param) =>
|
||||||
|
`/** ${param.description || param.name} */
|
||||||
|
${param.name}${param.required ? '' : '?'}:${param.dataType
|
||||||
|
.generateCode(param.getDsName())
|
||||||
|
.replace(/defs./g, '')},`,
|
||||||
|
)
|
||||||
|
.join('\n')}
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
getParamsDec(inter: Interface): string {
|
||||||
|
const hasQueryParams: boolean = inter.parameters.filter((p) => p.in === 'query').length > 0
|
||||||
|
let extPathParameters = extractPathParameters(inter.path)
|
||||||
|
extPathParameters = subSet(
|
||||||
|
extPathParameters,
|
||||||
|
inter.parameters.filter((p) => p.in === 'path').map((p) => p.name),
|
||||||
|
)
|
||||||
|
|
||||||
|
return `
|
||||||
|
${inter.parameters
|
||||||
|
.filter((p) => p.in === 'path')
|
||||||
|
.map(
|
||||||
|
(p) =>
|
||||||
|
`/** ${p.description || p.name} */
|
||||||
|
${p.name}:${p.dataType.generateCode(p.getDsName()).replace(/defs./g, '')},`,
|
||||||
|
)
|
||||||
|
.join('\n')}
|
||||||
|
${extPathParameters.map((p) => `${p} : string,`).join('\n')}
|
||||||
|
${inter.parameters
|
||||||
|
.filter((p) => p.in === 'body')
|
||||||
|
.map((p) => {
|
||||||
|
let temp = p.dataType.generateCode(p.getDsName()).replace(/defs./g, '')
|
||||||
|
temp =
|
||||||
|
temp.indexOf('VXETableSaveDTO') > 0 ? `VXETableSaveDTO<${temp.replace('VXETableSaveDTO', '')}>` : temp
|
||||||
|
return `/** ${p.description || '请求体'} */
|
||||||
|
${p.name}:${temp},`
|
||||||
|
})
|
||||||
|
.join('\n')}
|
||||||
|
${hasQueryParams ? 'params: Params,' : ''}`
|
||||||
|
}
|
||||||
|
genDataType(dsName: string, inter: Interface) {
|
||||||
|
const typeName = inter.response.typeName
|
||||||
|
if (!typeName || typeName === 'ResultVoid' || typeName === 'Void') {
|
||||||
|
return 'void'
|
||||||
|
}
|
||||||
|
if (typeName === 'string' || typeName === 'number' || typeName === 'boolean') {
|
||||||
|
return typeName
|
||||||
|
}
|
||||||
|
const dataType = typeName.replace('Result', '').replace('IPage', '').replace('Pagination', '').replace('List', '')
|
||||||
|
const originType = `${dsName}.${dataType}`
|
||||||
|
let genDataType = typeName.replace(dataType, originType).replace('Result', '')
|
||||||
|
if (typeName.indexOf('IPage') >= 0) {
|
||||||
|
genDataType = genDataType.replace('IPage', 'IPage<') + '>'
|
||||||
|
}
|
||||||
|
if (typeName.indexOf('Pagination') >= 0) {
|
||||||
|
genDataType = genDataType.replace('Pagination', 'Pagination<') + '>'
|
||||||
|
}
|
||||||
|
if (typeName.indexOf('VXETableSaveDTO') >= 0) {
|
||||||
|
genDataType = genDataType.replace('VXETableSaveDTO', 'VXETableSaveDTO<') + '>'
|
||||||
|
}
|
||||||
|
if (typeName.indexOf('List') >= 0) {
|
||||||
|
genDataType = genDataType.replace('List', 'Array<') + '>'
|
||||||
|
}
|
||||||
|
if (typeName.indexOf('Array') >= 0) {
|
||||||
|
genDataType = inter.response.generateCode(dsName).replace('defs.', '')
|
||||||
|
if (genDataType.indexOf('Codebook') >= 0) {
|
||||||
|
genDataType = genDataType.replace(dsName + '.', '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return genDataType
|
||||||
|
}
|
||||||
|
getInterfaceContent(inter: Interface) {
|
||||||
|
const bodyParmas = inter.getBodyParamsCode()
|
||||||
|
const bodyParam = inter.parameters.find((p) => p.in === 'body')
|
||||||
|
const bodyParmaName = bodyParam ? bodyParam.name : ''
|
||||||
|
const hasQueryParams = inter.parameters.filter((p) => p.in === 'query').length > 0
|
||||||
|
const hasResult = inter.response.typeName.indexOf('Result') >= 0
|
||||||
|
const hasIPage = inter.response.typeName.indexOf('IPage') >= 0
|
||||||
|
const hasPagination = inter.response.typeName.indexOf('Pagination') >= 0
|
||||||
|
const hasCodebook = inter.response.typeArgs[0] && inter.response.typeArgs[0].typeName.indexOf('Codebook') >= 0
|
||||||
|
const hasVxe = !!inter.parameters.find((p) => p.dataType.typeName.indexOf('VXETableSaveDTO') >= 0)
|
||||||
|
// const defTypes = this.getAllRefTypeNames(inter);
|
||||||
|
const imports = [
|
||||||
|
hasResult ? 'Result' : null,
|
||||||
|
hasIPage ? 'IPage' : null,
|
||||||
|
hasPagination ? 'Pagination' : null,
|
||||||
|
hasVxe ? 'VXETableSaveDTO' : null,
|
||||||
|
hasCodebook ? 'Codebook' : null,
|
||||||
|
].filter((item) => item !== null)
|
||||||
|
//url: \`/${getUrl(inter.getDsName())}${inter.path.replace(/{/g, '${')}\`,
|
||||||
|
const url = getUrl(inter.getDsName()) ? `/${getUrl(inter.getDsName())}` : ''
|
||||||
|
return `
|
||||||
|
/**
|
||||||
|
* @desc ${inter.description}
|
||||||
|
*/
|
||||||
|
import {defaultSuccess, defaultError, http} from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
${imports.length ? `import type { ${imports.join(',')} } from '@/api/api';` : ''};
|
||||||
|
${hasQueryParams ? `${this.getParamsTypeDec(inter)}` : ''}
|
||||||
|
|
||||||
|
export default async function(
|
||||||
|
${this.getParamsDec(inter)}
|
||||||
|
success: (data: ${this.genDataType(inter.getDsName(), inter)}) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError
|
||||||
|
) :Promise<void>{
|
||||||
|
return http({
|
||||||
|
method: '${inter.method}',
|
||||||
|
url: \`${url}${inter.path.replace(/{/g, '${')}\`,
|
||||||
|
${bodyParmas ? `data: ${bodyParmaName},` : ''}
|
||||||
|
${hasQueryParams ? `params,` : ''}
|
||||||
|
}).then((data: AxiosResponse<${this.genDataType(inter.getDsName(), inter)}, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
getModIndex(mod: Pont.Mod): string {
|
||||||
|
// const name: string = upperFirst(mod.name);
|
||||||
|
// const hasResult = mod.interfaces.find(inter => inter.response.typeName.indexOf('Result') >= 0);
|
||||||
|
// const hasIPage = mod.interfaces.find(inter => inter.response.typeName.indexOf('IPage') >= 0);
|
||||||
|
// const hasPagination = mod.interfaces.find(inter => inter.response.typeName.indexOf('Pagination') >= 0);
|
||||||
|
// const hasVxe = mod.interfaces.find(inter =>
|
||||||
|
// inter.parameters.find(p => p.dataType.typeName.indexOf('VXETableSaveDTO') >= 0),
|
||||||
|
// );
|
||||||
|
// const hasCodebook = mod.interfaces.find(
|
||||||
|
// inter => inter.response.typeArgs[0] && inter.response.typeArgs[0].typeName.indexOf('Codebook') >= 0,
|
||||||
|
// );
|
||||||
|
// const defTypes = this.getAllRefTypeNames(inter);
|
||||||
|
// const imports = [
|
||||||
|
// hasResult ? 'Result' : null,
|
||||||
|
// hasIPage ? 'IPage' : null,
|
||||||
|
// hasPagination ? 'Pagination' : null,
|
||||||
|
// hasVxe ? 'VXETableSaveDTO' : null,
|
||||||
|
// hasCodebook ? 'Codebook' : null,
|
||||||
|
// ].filter(item => item !== null);
|
||||||
|
return `
|
||||||
|
/**
|
||||||
|
* @description ${mod.description}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
${mod.interfaces
|
||||||
|
.map((inter) => {
|
||||||
|
return `import ${fixInterfaceName(inter.name)} from './${fixInterfaceName(inter.name)}';`
|
||||||
|
})
|
||||||
|
.join('\n')}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
${mod.interfaces.map((inter) => fixInterfaceName(inter.name)).join(', \n')}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
getModsIndex(): string {
|
||||||
|
const exportseg = `export default {
|
||||||
|
${this.dataSource.mods.map((mod) => mod.name).join(', \n')}
|
||||||
|
};`
|
||||||
|
return `
|
||||||
|
${this.dataSource.mods
|
||||||
|
.map((mod) => {
|
||||||
|
return `
|
||||||
|
import ${mod.name} from './${mod.name}';
|
||||||
|
`
|
||||||
|
})
|
||||||
|
.join('\n')}
|
||||||
|
${exportseg}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
21
prettier.config.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export default {
|
||||||
|
tabWidth: 2, // 缩进
|
||||||
|
useTabs: true, // 缩进方式
|
||||||
|
trailingComma: 'all',
|
||||||
|
semi: false, // 语句结尾是否加分号
|
||||||
|
singleQuote: true, // 单引号
|
||||||
|
printWidth: 120, // 换行长度
|
||||||
|
arrowParens: 'always', // 箭头函数参数
|
||||||
|
bracketSpacing: true, // 对象花括号内是否加空格
|
||||||
|
endOfLine: 'lf',
|
||||||
|
vueIndentScriptAndStyle: true, // vue文件内script和style标签缩进
|
||||||
|
htmlWhitespaceSensitivity: 'ignore',
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: '.prettierrc',
|
||||||
|
options: {
|
||||||
|
parser: 'json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
BIN
public/DS-DIGII-3.ttf
Normal file
BIN
public/logo.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
1
public/vite.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
79
src/App.vue
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<template>
|
||||||
|
<a-config-provider :theme="themeV" :locale="locale">
|
||||||
|
<div
|
||||||
|
:style="{
|
||||||
|
'--ant-primary-color': useAppStore().theme.primaryColor,
|
||||||
|
'--vxe-primary-color': useAppStore().theme.primaryColor,
|
||||||
|
'--vxe-loading-color': useAppStore().theme.primaryColor,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<a-watermark :content="config.layout.waterMark">
|
||||||
|
<router-view />
|
||||||
|
</a-watermark>
|
||||||
|
</div>
|
||||||
|
</a-config-provider>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useAppStore } from '@/stores/app'
|
||||||
|
import { theme } from 'ant-design-vue'
|
||||||
|
import { config } from './settings/application'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import 'dayjs/locale/zh-cn'
|
||||||
|
import { messages, setI18nLanguage } from './locales'
|
||||||
|
|
||||||
|
const locale = computed(() => {
|
||||||
|
return useAppStore().language === 'zh_CN' ? messages.zh_CN : messages.en_US
|
||||||
|
})
|
||||||
|
setI18nLanguage(useAppStore().language as 'zh_CN' | 'en_US')
|
||||||
|
useI18n().locale.value = useAppStore().language
|
||||||
|
const themeV = computed(() => {
|
||||||
|
return {
|
||||||
|
algorithm: app.layout.value.navTheme === 'real-dark' ? theme.darkAlgorithm : theme.defaultAlgorithm,
|
||||||
|
token: {
|
||||||
|
colorPrimary: useAppStore().theme.primaryColor,
|
||||||
|
borderRadius: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const app = storeToRefs(useAppStore())
|
||||||
|
useTitle(app.isCN.value ? config.title : config.name)
|
||||||
|
const colorWeak = computed(() => {
|
||||||
|
return app.layout.value.colorWeak
|
||||||
|
})
|
||||||
|
const gray = computed(() => {
|
||||||
|
return app.layout.value.gray
|
||||||
|
})
|
||||||
|
watch(
|
||||||
|
colorWeak,
|
||||||
|
(newVal) => {
|
||||||
|
const body = document.body
|
||||||
|
if (newVal) {
|
||||||
|
app.layout.value.gray = false
|
||||||
|
body.style.filter = 'invert(80%)'
|
||||||
|
} else {
|
||||||
|
body.style.removeProperty('filter')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
watch(
|
||||||
|
gray,
|
||||||
|
(newVal) => {
|
||||||
|
const body = document.body
|
||||||
|
if (newVal) {
|
||||||
|
app.layout.value.colorWeak = false
|
||||||
|
body.style.filter = 'grayscale(0.95)'
|
||||||
|
} else {
|
||||||
|
body.style.removeProperty('filter')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
<style lang="css">
|
||||||
|
:deep(.ant-btn-link) {
|
||||||
|
color: var(--ant-primary-color) !important;
|
||||||
|
}
|
||||||
|
</style>
|
198
src/api/acl/api.d.ts
vendored
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
declare namespace acl {
|
||||||
|
/**
|
||||||
|
* 权限
|
||||||
|
*/
|
||||||
|
export interface Permission {
|
||||||
|
/** createdTime */
|
||||||
|
createdTime?: string;
|
||||||
|
|
||||||
|
/** 权限描述 */
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/** id */
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
/** 权限key,英文 */
|
||||||
|
key?: string;
|
||||||
|
|
||||||
|
/** 权限keyPath,用来做业务,(父级keyPath.key) */
|
||||||
|
keyPath?: string;
|
||||||
|
|
||||||
|
/** 权限名称,中文用来做标识 */
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
/** 父权限key */
|
||||||
|
parentKey?: string;
|
||||||
|
|
||||||
|
/** 权限类型 */
|
||||||
|
type?: 'MENU' | 'BUTTON' | 'OTHER';
|
||||||
|
|
||||||
|
/** typeInfo */
|
||||||
|
typeInfo?: acl.Codebook;
|
||||||
|
|
||||||
|
/** updatedTime */
|
||||||
|
updatedTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限信息,包含是否选中标识
|
||||||
|
*/
|
||||||
|
export interface PermissionInfo {
|
||||||
|
/** createdTime */
|
||||||
|
createdTime?: string;
|
||||||
|
|
||||||
|
/** 权限描述 */
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/** id */
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
/** 权限key,英文 */
|
||||||
|
key?: string;
|
||||||
|
|
||||||
|
/** 权限keyPath,用来做业务,(父级keyPath.key) */
|
||||||
|
keyPath?: string;
|
||||||
|
|
||||||
|
/** 权限名称,中文用来做标识 */
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
/** 父权限key */
|
||||||
|
parentKey?: string;
|
||||||
|
|
||||||
|
/** 权限是否选中标识 */
|
||||||
|
selected: boolean;
|
||||||
|
|
||||||
|
/** 权限类型 */
|
||||||
|
type?: 'MENU' | 'BUTTON' | 'OTHER';
|
||||||
|
|
||||||
|
/** typeInfo */
|
||||||
|
typeInfo?: acl.Codebook;
|
||||||
|
|
||||||
|
/** updatedTime */
|
||||||
|
updatedTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色
|
||||||
|
*/
|
||||||
|
export interface Role {
|
||||||
|
/** createdTime */
|
||||||
|
createdTime?: string;
|
||||||
|
|
||||||
|
/** 角色描述 */
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/** id */
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
/** 角色key,英文,用来做业务 */
|
||||||
|
key: string;
|
||||||
|
|
||||||
|
/** 角色名称,中文用来做标识 */
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
/** updatedTime */
|
||||||
|
updatedTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色信息,包含是否选中标识
|
||||||
|
*/
|
||||||
|
export interface RoleInfo {
|
||||||
|
/** createdTime */
|
||||||
|
createdTime?: string;
|
||||||
|
|
||||||
|
/** 角色描述 */
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/** id */
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
/** 角色key,英文,用来做业务 */
|
||||||
|
key: string;
|
||||||
|
|
||||||
|
/** 角色名称,中文用来做标识 */
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
/** 角色是否选中标识 */
|
||||||
|
selected: boolean;
|
||||||
|
|
||||||
|
/** updatedTime */
|
||||||
|
updatedTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 树
|
||||||
|
*/
|
||||||
|
export interface TreeString {
|
||||||
|
/** 下级列表 */
|
||||||
|
children?: Array<acl.TreeString>;
|
||||||
|
|
||||||
|
/** 描述 */
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/** ID */
|
||||||
|
key: string;
|
||||||
|
|
||||||
|
/** 名称 */
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/** originData */
|
||||||
|
originData: acl.TreeableString;
|
||||||
|
|
||||||
|
/** 父级 ID */
|
||||||
|
parentKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原始数据
|
||||||
|
*/
|
||||||
|
export interface TreeableString {
|
||||||
|
/** description */
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/** key */
|
||||||
|
key?: string;
|
||||||
|
|
||||||
|
/** name */
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
/** parentKey */
|
||||||
|
parentKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户
|
||||||
|
*/
|
||||||
|
export interface User {
|
||||||
|
/** createdTime */
|
||||||
|
createdTime?: string;
|
||||||
|
|
||||||
|
/** 邮箱 */
|
||||||
|
email?: string;
|
||||||
|
|
||||||
|
/** 真实姓名 */
|
||||||
|
fullName?: string;
|
||||||
|
|
||||||
|
/** id */
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
/** 手机号 */
|
||||||
|
mobile?: string;
|
||||||
|
|
||||||
|
/** 用户名 */
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/** 密码 */
|
||||||
|
password?: string;
|
||||||
|
|
||||||
|
/** 性别 */
|
||||||
|
sex?: 'MALE' | 'FEMALE';
|
||||||
|
|
||||||
|
/** sexInfo */
|
||||||
|
sexInfo?: acl.Codebook;
|
||||||
|
|
||||||
|
/** updatedTime */
|
||||||
|
updatedTime?: string;
|
||||||
|
}
|
||||||
|
}
|
11
src/api/acl/mods/index.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import permission from './permission';
|
||||||
|
|
||||||
|
import role from './role';
|
||||||
|
|
||||||
|
import user from './user';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
permission,
|
||||||
|
role,
|
||||||
|
user,
|
||||||
|
};
|
22
src/api/acl/mods/permission/add.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @desc 增加权限
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: acl.Permission,
|
||||||
|
|
||||||
|
success: (data: acl.Permission) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'post',
|
||||||
|
url: `/permission`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<acl.Permission, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
22
src/api/acl/mods/permission/batchInitPermissions.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @desc 批量新增权限
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: Array<acl.Permission>,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'post',
|
||||||
|
url: `/batch-init-permissions`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
22
src/api/acl/mods/permission/batchSyncPermissions.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @desc 增量新增权限
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: Array<acl.Permission>,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'patch',
|
||||||
|
url: `/batch-sync-permissions`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/acl/mods/permission/deletePermission.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 删除权限
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 权限key */
|
||||||
|
key: string,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'delete',
|
||||||
|
url: `/permission/${key}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/acl/mods/permission/detail.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 权限详情
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 权限id */
|
||||||
|
id: number,
|
||||||
|
|
||||||
|
success: (data: acl.Permission) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/permission/${id}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<acl.Permission, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
25
src/api/acl/mods/permission/index.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @description 权限
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import batchInitPermissions from './batchInitPermissions';
|
||||||
|
import batchSyncPermissions from './batchSyncPermissions';
|
||||||
|
import add from './add';
|
||||||
|
import update from './update';
|
||||||
|
import types from './types';
|
||||||
|
import detail from './detail';
|
||||||
|
import deletePermission from './deletePermission';
|
||||||
|
import permissions from './permissions';
|
||||||
|
import permissionTree from './permissionTree';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
batchInitPermissions,
|
||||||
|
batchSyncPermissions,
|
||||||
|
add,
|
||||||
|
update,
|
||||||
|
types,
|
||||||
|
detail,
|
||||||
|
deletePermission,
|
||||||
|
permissions,
|
||||||
|
permissionTree,
|
||||||
|
};
|
26
src/api/acl/mods/permission/permissionTree.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* @desc 权限树
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export interface Params {
|
||||||
|
/** 强制拉取 */
|
||||||
|
force?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function (
|
||||||
|
params: Params,
|
||||||
|
success: (data: Array<acl.TreeString>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/permissions/tree`,
|
||||||
|
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<Array<acl.TreeString>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
26
src/api/acl/mods/permission/permissions.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* @desc 权限列表
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export interface Params {
|
||||||
|
/** 强制拉取 */
|
||||||
|
force?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function (
|
||||||
|
params: Params,
|
||||||
|
success: (data: Array<acl.Permission>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/permissions`,
|
||||||
|
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<Array<acl.Permission>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
20
src/api/acl/mods/permission/types.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @desc 权限类型
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
import type { Codebook } from '@/api/api';
|
||||||
|
|
||||||
|
export default async function (
|
||||||
|
success: (data: Array<Codebook>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/permission/types`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<Array<Codebook>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
22
src/api/acl/mods/permission/update.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @desc 更新权限
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: acl.Permission,
|
||||||
|
|
||||||
|
success: (data: acl.Permission) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'patch',
|
||||||
|
url: `/permission`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<acl.Permission, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/acl/mods/role/deleteRole.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 删除角色
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 角色key */
|
||||||
|
key: string,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'delete',
|
||||||
|
url: `/role/${key}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/acl/mods/role/detail.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 角色详情
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 角色id */
|
||||||
|
id: number,
|
||||||
|
|
||||||
|
success: (data: acl.Role) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/role/${id}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<acl.Role, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
25
src/api/acl/mods/role/grant.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @desc 为指定角色授权
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 角色key */
|
||||||
|
key: string,
|
||||||
|
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: Array<string>,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'post',
|
||||||
|
url: `/role/${key}/permissions`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/acl/mods/role/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @description 角色
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import saveOrUpdateRole from './saveOrUpdateRole';
|
||||||
|
import detail from './detail';
|
||||||
|
import deleteRole from './deleteRole';
|
||||||
|
import permissionInfos from './permissionInfos';
|
||||||
|
import permissions from './permissions';
|
||||||
|
import grant from './grant';
|
||||||
|
import roles from './roles';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
saveOrUpdateRole,
|
||||||
|
detail,
|
||||||
|
deleteRole,
|
||||||
|
permissionInfos,
|
||||||
|
permissions,
|
||||||
|
grant,
|
||||||
|
roles,
|
||||||
|
};
|
21
src/api/acl/mods/role/permissionInfos.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 查询用于授权的权限信息
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 角色key */
|
||||||
|
key: string,
|
||||||
|
|
||||||
|
success: (data: Array<acl.PermissionInfo>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/role/${key}/permission-infos`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<Array<acl.PermissionInfo>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/acl/mods/role/permissions.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 查询角色权限
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 角色key */
|
||||||
|
key: string,
|
||||||
|
|
||||||
|
success: (data: Array<acl.Permission>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/role/${key}/permissions`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<Array<acl.Permission>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
31
src/api/acl/mods/role/roles.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* @desc 分页查询角色
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
import type { IPage } from '@/api/api';
|
||||||
|
export interface Params {
|
||||||
|
/** 页码 */
|
||||||
|
page?: number;
|
||||||
|
/** 页面大小 */
|
||||||
|
size?: number;
|
||||||
|
/** 搜索关键词 */
|
||||||
|
key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function (
|
||||||
|
params: Params,
|
||||||
|
success: (data: IPage<acl.Role>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/roles`,
|
||||||
|
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<IPage<acl.Role>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
22
src/api/acl/mods/role/saveOrUpdateRole.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @desc 增加/编辑角色
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: acl.Role,
|
||||||
|
|
||||||
|
success: (data: acl.Role) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'put',
|
||||||
|
url: `/role`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<acl.Role, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/acl/mods/user/deleteUser.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 删除用户
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 用户id */
|
||||||
|
id: number,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'delete',
|
||||||
|
url: `/user/${id}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/acl/mods/user/detail.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 用户详情
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 用户id */
|
||||||
|
id: number,
|
||||||
|
|
||||||
|
success: (data: acl.User) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/user/${id}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<acl.User, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
25
src/api/acl/mods/user/grant.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @desc 为指定用户授权
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 用户名 */
|
||||||
|
name: string,
|
||||||
|
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: Array<string>,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'post',
|
||||||
|
url: `/user/${name}/permissions`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
25
src/api/acl/mods/user/grantRole.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @desc 为指定用户设置角色
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 用户名 */
|
||||||
|
name: string,
|
||||||
|
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: Array<string>,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'post',
|
||||||
|
url: `/user/${name}/roles`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
29
src/api/acl/mods/user/index.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* @description 用户
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import saveOrUpdateUser from './saveOrUpdateUser';
|
||||||
|
import sexes from './sexes';
|
||||||
|
import detail from './detail';
|
||||||
|
import deleteUser from './deleteUser';
|
||||||
|
import resetPassword from './resetPassword';
|
||||||
|
import permissionInfos from './permissionInfos';
|
||||||
|
import permissions from './permissions';
|
||||||
|
import grant from './grant';
|
||||||
|
import roleInfos from './roleInfos';
|
||||||
|
import grantRole from './grantRole';
|
||||||
|
import users from './users';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
saveOrUpdateUser,
|
||||||
|
sexes,
|
||||||
|
detail,
|
||||||
|
deleteUser,
|
||||||
|
resetPassword,
|
||||||
|
permissionInfos,
|
||||||
|
permissions,
|
||||||
|
grant,
|
||||||
|
roleInfos,
|
||||||
|
grantRole,
|
||||||
|
users,
|
||||||
|
};
|
21
src/api/acl/mods/user/permissionInfos.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 查询用于授权的权限信息
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 用户名 */
|
||||||
|
name: string,
|
||||||
|
|
||||||
|
success: (data: Array<acl.PermissionInfo>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/user/${name}/permission-infos`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<Array<acl.PermissionInfo>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/acl/mods/user/permissions.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 查询用户权限
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 用户名 */
|
||||||
|
name: string,
|
||||||
|
|
||||||
|
success: (data: Array<acl.Permission>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/user/${name}/permissions`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<Array<acl.Permission>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/acl/mods/user/resetPassword.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 重置密码
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 用户名 */
|
||||||
|
name: string,
|
||||||
|
|
||||||
|
success: (data: string) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'patch',
|
||||||
|
url: `/user/${name}/password`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<string, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/acl/mods/user/roleInfos.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 查询用户权限
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 用户名 */
|
||||||
|
name: string,
|
||||||
|
|
||||||
|
success: (data: Array<acl.RoleInfo>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/user/${name}/role-infos`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<Array<acl.RoleInfo>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
22
src/api/acl/mods/user/saveOrUpdateUser.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @desc 增加/编辑用户
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: acl.User,
|
||||||
|
|
||||||
|
success: (data: acl.User) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'put',
|
||||||
|
url: `/user`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<acl.User, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
20
src/api/acl/mods/user/sexes.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @desc 用户性别
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
import type { Codebook } from '@/api/api';
|
||||||
|
|
||||||
|
export default async function (
|
||||||
|
success: (data: Array<Codebook>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/user/sexes`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<Array<Codebook>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
33
src/api/acl/mods/user/users.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* @desc 分页查询用户
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
import type { IPage } from '@/api/api';
|
||||||
|
export interface Params {
|
||||||
|
/** 页码 */
|
||||||
|
page?: number;
|
||||||
|
/** 页面大小 */
|
||||||
|
size?: number;
|
||||||
|
/** 性别 */
|
||||||
|
sex?: 'MALE' | 'FEMALE';
|
||||||
|
/** 搜索关键词 */
|
||||||
|
key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function (
|
||||||
|
params: Params,
|
||||||
|
success: (data: IPage<acl.User>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/users`,
|
||||||
|
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<IPage<acl.User>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
4329
src/api/api-lock.json
Normal file
118
src/api/api.d.ts
vendored
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/**
|
||||||
|
* mybatis-plus 分页对象
|
||||||
|
*/
|
||||||
|
export interface IPage<T> {
|
||||||
|
/**
|
||||||
|
* 当前页面
|
||||||
|
*/
|
||||||
|
current: number;
|
||||||
|
/**
|
||||||
|
* 总页数
|
||||||
|
*/
|
||||||
|
pages?: number;
|
||||||
|
/**
|
||||||
|
* 数据记录
|
||||||
|
*/
|
||||||
|
records?: Array<T>;
|
||||||
|
/**
|
||||||
|
* 分页大小
|
||||||
|
*/
|
||||||
|
size: number;
|
||||||
|
/**
|
||||||
|
* 数据总条数
|
||||||
|
*/
|
||||||
|
total?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Nutz 分页对象
|
||||||
|
*/
|
||||||
|
export interface Pagination<T> {
|
||||||
|
/**
|
||||||
|
* 数据记录
|
||||||
|
*/
|
||||||
|
dataList: Array<T>;
|
||||||
|
/**
|
||||||
|
* 是否首页
|
||||||
|
*/
|
||||||
|
first?: boolean;
|
||||||
|
/**
|
||||||
|
* 是否尾页
|
||||||
|
*/
|
||||||
|
last?: boolean;
|
||||||
|
/**
|
||||||
|
* 偏移量
|
||||||
|
*/
|
||||||
|
offset?: number;
|
||||||
|
/**
|
||||||
|
* 总页数
|
||||||
|
*/
|
||||||
|
pageCount: number;
|
||||||
|
/**
|
||||||
|
* 当前页码
|
||||||
|
*/
|
||||||
|
pageNumber: number;
|
||||||
|
/**
|
||||||
|
* 页面大小
|
||||||
|
*/
|
||||||
|
pageSize: number;
|
||||||
|
/**
|
||||||
|
* sql参数
|
||||||
|
*/
|
||||||
|
paras: Record<string, unknown>;
|
||||||
|
/**
|
||||||
|
* 数据记录总数
|
||||||
|
*/
|
||||||
|
recordCount?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* VXE 数据结构
|
||||||
|
*/
|
||||||
|
export interface VXETableSaveDTO<T> {
|
||||||
|
/**
|
||||||
|
* 新增记录数据
|
||||||
|
*/
|
||||||
|
insertRecords: Array<T>;
|
||||||
|
/**
|
||||||
|
* 删除记录数据
|
||||||
|
*/
|
||||||
|
removeRecords: Array<T>;
|
||||||
|
/**
|
||||||
|
* 更新记录数据
|
||||||
|
*/
|
||||||
|
updateRecords: Array<T>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 枚举码本
|
||||||
|
*/
|
||||||
|
export interface Codebook {
|
||||||
|
/** 码本编码 */
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
/** 码本名称 */
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
/** 码本值 */
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局错误
|
||||||
|
*/
|
||||||
|
export interface GlobalError {
|
||||||
|
/** 错误码 */
|
||||||
|
code?: number;
|
||||||
|
|
||||||
|
/** 错误信息 */
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* NutMap
|
||||||
|
*/
|
||||||
|
type NutMap<Key extends string, Value = unknown> = {
|
||||||
|
[key in Key]: Value;
|
||||||
|
};
|
||||||
|
// 导入其他模块
|
||||||
|
/// <reference path="./auth/api.d.ts" />
|
||||||
|
/// <reference path="./acl/api.d.ts" />
|
||||||
|
/// <reference path="./dictionary/api.d.ts" />
|
||||||
|
/// <reference path="./material/api.d.ts" />
|
71
src/api/auth/api.d.ts
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
declare namespace auth {
|
||||||
|
/**
|
||||||
|
* AuthUser
|
||||||
|
*/
|
||||||
|
export interface AuthUser {
|
||||||
|
/** anonymous */
|
||||||
|
anonymous?: boolean;
|
||||||
|
|
||||||
|
/** 用户头像 */
|
||||||
|
avatar?: string;
|
||||||
|
|
||||||
|
/** 邮箱 */
|
||||||
|
email?: string;
|
||||||
|
|
||||||
|
/** extInfo */
|
||||||
|
extInfo?: auth.NutMap;
|
||||||
|
|
||||||
|
/** 姓名 */
|
||||||
|
fullName: string;
|
||||||
|
|
||||||
|
/** 电话 */
|
||||||
|
mobile?: string;
|
||||||
|
|
||||||
|
/** 密码 */
|
||||||
|
password: string;
|
||||||
|
|
||||||
|
/** 权限列表 */
|
||||||
|
permissions: Array<string>;
|
||||||
|
|
||||||
|
/** jwt refreshToken */
|
||||||
|
refreshToken: string;
|
||||||
|
|
||||||
|
/** 角色列表 */
|
||||||
|
roles: Array<string>;
|
||||||
|
|
||||||
|
/** 性别 */
|
||||||
|
sex?: 'MALE' | 'FEMALE';
|
||||||
|
|
||||||
|
/** sexInfo */
|
||||||
|
sexInfo?: auth.Codebook;
|
||||||
|
|
||||||
|
/** jwt Token */
|
||||||
|
token: string;
|
||||||
|
|
||||||
|
/** 用户名 */
|
||||||
|
userName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录实体
|
||||||
|
*/
|
||||||
|
export interface LoginDTO {
|
||||||
|
/** 验证码 */
|
||||||
|
captcha?: string;
|
||||||
|
|
||||||
|
/** 手机号 */
|
||||||
|
mobile?: string;
|
||||||
|
|
||||||
|
/** 密码 */
|
||||||
|
password?: string;
|
||||||
|
|
||||||
|
/** 登录类型 */
|
||||||
|
type: 'ACCOUNT' | 'WECHAT';
|
||||||
|
|
||||||
|
/** 用户名 */
|
||||||
|
userName?: string;
|
||||||
|
|
||||||
|
/** uuid */
|
||||||
|
uuid?: string;
|
||||||
|
}
|
||||||
|
}
|
18
src/api/auth/mods/auth/currentUser.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @desc 当前用户
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
success: (data: auth.AuthUser) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/auth/current-user`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<auth.AuthUser, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
13
src/api/auth/mods/auth/index.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @description 登录
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import currentUser from './currentUser';
|
||||||
|
import login from './login';
|
||||||
|
import logout from './logout';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
currentUser,
|
||||||
|
login,
|
||||||
|
logout,
|
||||||
|
};
|
22
src/api/auth/mods/auth/login.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @desc 登录
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: auth.LoginDTO,
|
||||||
|
|
||||||
|
success: (data: auth.AuthUser) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'post',
|
||||||
|
url: `/auth/login`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<auth.AuthUser, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
18
src/api/auth/mods/auth/logout.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @desc 登出
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'post',
|
||||||
|
url: `/auth/logout`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
5
src/api/auth/mods/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import auth from './auth';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
auth,
|
||||||
|
};
|
62
src/api/dictionary/api.d.ts
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
declare namespace dictionary {
|
||||||
|
/**
|
||||||
|
* 码本数据
|
||||||
|
*/
|
||||||
|
export interface Dictionary {
|
||||||
|
/** createdTime */
|
||||||
|
createdTime?: string;
|
||||||
|
|
||||||
|
/** 描述 */
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/** 禁用标识 */
|
||||||
|
disabled?: boolean;
|
||||||
|
|
||||||
|
/** 分组Key */
|
||||||
|
groupKey?: string;
|
||||||
|
|
||||||
|
/** id */
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
/** 序号 */
|
||||||
|
index?: number;
|
||||||
|
|
||||||
|
/** key */
|
||||||
|
key?: string;
|
||||||
|
|
||||||
|
/** 上级Key */
|
||||||
|
parentKey?: string;
|
||||||
|
|
||||||
|
/** updatedTime */
|
||||||
|
updatedTime?: string;
|
||||||
|
|
||||||
|
/** value */
|
||||||
|
value?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 码本分组
|
||||||
|
*/
|
||||||
|
export interface Group {
|
||||||
|
/** createdTime */
|
||||||
|
createdTime?: string;
|
||||||
|
|
||||||
|
/** 分组描述 */
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/** 禁用标识 */
|
||||||
|
disabled?: boolean;
|
||||||
|
|
||||||
|
/** id */
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
/** 分组唯一键 */
|
||||||
|
key: string;
|
||||||
|
|
||||||
|
/** 分组名称 */
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
/** updatedTime */
|
||||||
|
updatedTime?: string;
|
||||||
|
}
|
||||||
|
}
|
23
src/api/dictionary/mods/dictionary/deleteDictionary.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* @desc 删除码本数据
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 码本分组key */
|
||||||
|
group: string,
|
||||||
|
/** 码本数据key */
|
||||||
|
key: string,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'delete',
|
||||||
|
url: `/group/${group}/dictionary/${key}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/dictionary/mods/dictionary/deleteGroup.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 删除码本分组
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 码本分组key */
|
||||||
|
key: string,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'delete',
|
||||||
|
url: `/group/${key}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/dictionary/mods/dictionary/dictionaries.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 分组下的数据字典列表
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 码本分组key */
|
||||||
|
group: string,
|
||||||
|
|
||||||
|
success: (data: Array<dictionary.Dictionary>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/group/${group}/dictionaries`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<Array<dictionary.Dictionary>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/dictionary/mods/dictionary/groupDetail.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 码本分组详情
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 码本分组id */
|
||||||
|
id: number,
|
||||||
|
|
||||||
|
success: (data: dictionary.Group) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/group/${id}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<dictionary.Group, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
31
src/api/dictionary/mods/dictionary/groups.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* @desc 分页查询码本分组
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
import type { IPage } from '@/api/api';
|
||||||
|
export interface Params {
|
||||||
|
/** 页码 */
|
||||||
|
page?: number;
|
||||||
|
/** 页面大小 */
|
||||||
|
size?: number;
|
||||||
|
/** 搜索关键词 */
|
||||||
|
key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function (
|
||||||
|
params: Params,
|
||||||
|
success: (data: IPage<dictionary.Group>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/groups`,
|
||||||
|
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<IPage<dictionary.Group>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/dictionary/mods/dictionary/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @description 码本数据
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import saveOrUpdateGroup from './saveOrUpdateGroup';
|
||||||
|
import dictionaries from './dictionaries';
|
||||||
|
import saveOrUpdateDictionary from './saveOrUpdateDictionary';
|
||||||
|
import deleteDictionary from './deleteDictionary';
|
||||||
|
import groupDetail from './groupDetail';
|
||||||
|
import deleteGroup from './deleteGroup';
|
||||||
|
import groups from './groups';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
saveOrUpdateGroup,
|
||||||
|
dictionaries,
|
||||||
|
saveOrUpdateDictionary,
|
||||||
|
deleteDictionary,
|
||||||
|
groupDetail,
|
||||||
|
deleteGroup,
|
||||||
|
groups,
|
||||||
|
};
|
25
src/api/dictionary/mods/dictionary/saveOrUpdateDictionary.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @desc 增加/编辑码本数据
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 码本分组key */
|
||||||
|
group: string,
|
||||||
|
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: dictionary.Dictionary,
|
||||||
|
|
||||||
|
success: (data: dictionary.Dictionary) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'put',
|
||||||
|
url: `/group/${group}/dictionary`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<dictionary.Dictionary, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
22
src/api/dictionary/mods/dictionary/saveOrUpdateGroup.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @desc 增加/编辑码本分组
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: dictionary.Group,
|
||||||
|
|
||||||
|
success: (data: dictionary.Group) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'put',
|
||||||
|
url: `/group`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<dictionary.Group, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
5
src/api/dictionary/mods/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import dictionary from './dictionary';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
dictionary,
|
||||||
|
};
|
19
src/api/index.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import type { App } from 'vue';
|
||||||
|
import authApi from './auth/mods';
|
||||||
|
|
||||||
|
import aclApi from './acl/mods';
|
||||||
|
|
||||||
|
import dictionaryApi from './dictionary/mods';
|
||||||
|
|
||||||
|
import materialApi from './material/mods';
|
||||||
|
|
||||||
|
export const api = {
|
||||||
|
authApi,
|
||||||
|
aclApi,
|
||||||
|
dictionaryApi,
|
||||||
|
materialApi,
|
||||||
|
install: (app: App) => {
|
||||||
|
app.config.globalProperties.$api = api;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default api;
|
105
src/api/material/api.d.ts
vendored
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
declare namespace material {
|
||||||
|
/**
|
||||||
|
* 申请单明细
|
||||||
|
*/
|
||||||
|
export interface ApplyDetail {
|
||||||
|
/** 申请单ID */
|
||||||
|
applyId?: number;
|
||||||
|
|
||||||
|
/** 确认数量 */
|
||||||
|
confirmQuantity?: number;
|
||||||
|
|
||||||
|
/** createdTime */
|
||||||
|
createdTime?: string;
|
||||||
|
|
||||||
|
/** 异常情况说明 */
|
||||||
|
exceptionRemark?: string;
|
||||||
|
|
||||||
|
/** id */
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
/** 物料Id */
|
||||||
|
materialId?: number;
|
||||||
|
|
||||||
|
/** 申请数量 */
|
||||||
|
quantity?: number;
|
||||||
|
|
||||||
|
/** updatedTime */
|
||||||
|
updatedTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入库/出库 申请单
|
||||||
|
*/
|
||||||
|
export interface ApplyForm {
|
||||||
|
/** 申请人 */
|
||||||
|
applicant?: string;
|
||||||
|
|
||||||
|
/** 申请日期 */
|
||||||
|
applyDate?: string;
|
||||||
|
|
||||||
|
/** createdTime */
|
||||||
|
createdTime?: string;
|
||||||
|
|
||||||
|
/** 异常说明 */
|
||||||
|
exceptionExplain?: string;
|
||||||
|
|
||||||
|
/** id */
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
/** 是否确认 */
|
||||||
|
isConfirm?: boolean;
|
||||||
|
|
||||||
|
/** 类型 */
|
||||||
|
type?: number;
|
||||||
|
|
||||||
|
/** updatedTime */
|
||||||
|
updatedTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请单
|
||||||
|
*/
|
||||||
|
export interface ApplyInfo {
|
||||||
|
/** applyDetails */
|
||||||
|
applyDetails?: Array<material.ApplyDetail>;
|
||||||
|
|
||||||
|
/** applyForm */
|
||||||
|
applyForm?: material.ApplyForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物料信息
|
||||||
|
*/
|
||||||
|
export interface Material {
|
||||||
|
/** 赋码规则 */
|
||||||
|
assignRule?: number;
|
||||||
|
|
||||||
|
/** 编码 */
|
||||||
|
code?: string;
|
||||||
|
|
||||||
|
/** createdTime */
|
||||||
|
createdTime?: string;
|
||||||
|
|
||||||
|
/** 备注 */
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/** id */
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
/** 名称 */
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
/** 规格 */
|
||||||
|
spec?: string;
|
||||||
|
|
||||||
|
/** 库存数量 */
|
||||||
|
stock?: number;
|
||||||
|
|
||||||
|
/** 类型 */
|
||||||
|
type?: string;
|
||||||
|
|
||||||
|
/** updatedTime */
|
||||||
|
updatedTime?: string;
|
||||||
|
}
|
||||||
|
}
|
33
src/api/material/mods/apply/applies.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* @desc 分页查询申请单列表
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
import type { IPage } from '@/api/api';
|
||||||
|
export interface Params {
|
||||||
|
/** 类型 */
|
||||||
|
type: number;
|
||||||
|
/** 页码 */
|
||||||
|
page?: number;
|
||||||
|
/** 页面大小 */
|
||||||
|
size?: number;
|
||||||
|
/** 搜索关键词 */
|
||||||
|
key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function (
|
||||||
|
params: Params,
|
||||||
|
success: (data: IPage<material.ApplyForm>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/apply`,
|
||||||
|
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<IPage<material.ApplyForm>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/material/mods/apply/detail.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 查询申请单详情
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 类型 */
|
||||||
|
applyId: number,
|
||||||
|
|
||||||
|
success: (data: material.ApplyInfo) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/apply/${applyId}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<material.ApplyInfo, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
13
src/api/material/mods/apply/index.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @description 入库/出库 申请单管理
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import applies from './applies';
|
||||||
|
import saveOrUpdateApply from './saveOrUpdateApply';
|
||||||
|
import detail from './detail';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
applies,
|
||||||
|
saveOrUpdateApply,
|
||||||
|
detail,
|
||||||
|
};
|
34
src/api/material/mods/apply/materials.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @desc 分页查询申请单列表
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
import type { IPage } from '@/api/api';
|
||||||
|
export interface Params {
|
||||||
|
/** 页码 */
|
||||||
|
page?: number;
|
||||||
|
/** 页面大小 */
|
||||||
|
size?: number;
|
||||||
|
/** 搜索关键词 */
|
||||||
|
key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function (
|
||||||
|
/** 类型 */
|
||||||
|
type: number,
|
||||||
|
|
||||||
|
params: Params,
|
||||||
|
success: (data: IPage<material.ApplyForm>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/apply/${type}`,
|
||||||
|
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<IPage<material.ApplyForm>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
22
src/api/material/mods/apply/saveOrUpdateApply.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @desc 增加/编辑申请单
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: material.ApplyInfo,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'put',
|
||||||
|
url: `/apply`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
8
src/api/material/mods/index.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import apply from './apply';
|
||||||
|
|
||||||
|
import material from './material';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
apply,
|
||||||
|
material,
|
||||||
|
};
|
18
src/api/material/mods/material/all.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @desc 查询所有物料列表
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
success: (data: Array<material.Material>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/material/list`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<Array<material.Material>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/material/mods/material/deleteMaterial.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 删除物料
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 物料id */
|
||||||
|
id: number,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'delete',
|
||||||
|
url: `/material/${id}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/material/mods/material/deleteUser.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 删除物料
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 物料id */
|
||||||
|
id: number,
|
||||||
|
|
||||||
|
success: (data: void) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'delete',
|
||||||
|
url: `/material/${id}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<void, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
21
src/api/material/mods/material/detail.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @desc 物料详情
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 物料id */
|
||||||
|
id: number,
|
||||||
|
|
||||||
|
success: (data: material.Material) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/material/${id}`,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<material.Material, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
17
src/api/material/mods/material/index.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @description 物料管理
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import saveOrUpdateMaterial from './saveOrUpdateMaterial';
|
||||||
|
import all from './all';
|
||||||
|
import detail from './detail';
|
||||||
|
import deleteMaterial from './deleteMaterial';
|
||||||
|
import materials from './materials';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
saveOrUpdateMaterial,
|
||||||
|
all,
|
||||||
|
detail,
|
||||||
|
deleteMaterial,
|
||||||
|
materials,
|
||||||
|
};
|
33
src/api/material/mods/material/materials.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* @desc 分页查询物料列表
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
import type { IPage } from '@/api/api';
|
||||||
|
export interface Params {
|
||||||
|
/** 页码 */
|
||||||
|
page?: number;
|
||||||
|
/** 页面大小 */
|
||||||
|
size?: number;
|
||||||
|
/** 类型 */
|
||||||
|
type?: number;
|
||||||
|
/** 搜索关键词 */
|
||||||
|
key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function (
|
||||||
|
params: Params,
|
||||||
|
success: (data: IPage<material.Material>) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'get',
|
||||||
|
url: `/materials`,
|
||||||
|
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<IPage<material.Material>, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
22
src/api/material/mods/material/saveOrUpdateMaterial.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @desc 增加/编辑物料
|
||||||
|
*/
|
||||||
|
import { defaultSuccess, defaultError, http } from '@/plugins/axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
export default async function (
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody: material.Material,
|
||||||
|
|
||||||
|
success: (data: material.Material) => void = defaultSuccess,
|
||||||
|
fail: (error: { code: string; error?: string }) => void = defaultError,
|
||||||
|
): Promise<void> {
|
||||||
|
return http({
|
||||||
|
method: 'put',
|
||||||
|
url: `/material`,
|
||||||
|
data: requestBody,
|
||||||
|
})
|
||||||
|
.then((data: AxiosResponse<material.Material, unknown>) => {
|
||||||
|
success(data.data);
|
||||||
|
})
|
||||||
|
.catch((error: { code: string; error?: string }) => fail(error));
|
||||||
|
}
|
72
src/api/pontCore.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* @description pont内置请求单例
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PontCoreManager {
|
||||||
|
static singleInstance = null as PontCoreManager;
|
||||||
|
|
||||||
|
static getSignleInstance() {
|
||||||
|
if (!PontCoreManager.singleInstance) {
|
||||||
|
PontCoreManager.singleInstance = new PontCoreManager();
|
||||||
|
return PontCoreManager.singleInstance;
|
||||||
|
}
|
||||||
|
return PontCoreManager.singleInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fetch请求
|
||||||
|
* @param url 请求url
|
||||||
|
* @param options fetch 请求配置
|
||||||
|
*/
|
||||||
|
fetch(url: string, options = {}) {
|
||||||
|
return fetch(url, options).then((res) => {
|
||||||
|
return res.json();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用外部传入的请求方法替换默认的fetch请求
|
||||||
|
*/
|
||||||
|
useFetch(fetch: (url: string, options?: any) => Promise<any>) {
|
||||||
|
if (typeof fetch !== 'function') {
|
||||||
|
console.error('fetch should be a function ');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fetch = fetch;
|
||||||
|
}
|
||||||
|
|
||||||
|
getUrl(path: string, queryParams: any, method: string) {
|
||||||
|
const params = {
|
||||||
|
...(queryParams || ({} as any)),
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = path.replace(/\{([^\\}]*(?:\\.[^\\}]*)*)\}/gm, (match, key) => {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
key = key.trim();
|
||||||
|
|
||||||
|
if (params[key] !== undefined) {
|
||||||
|
const value = params[key];
|
||||||
|
delete params[key];
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
console.warn('Please set value for template key: ', key);
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
const paramStr = Object.keys(params)
|
||||||
|
.map((key) => {
|
||||||
|
return params[key] === undefined ? '' : `${key}=${params[key]}`;
|
||||||
|
})
|
||||||
|
.filter((id) => id)
|
||||||
|
.join('&');
|
||||||
|
|
||||||
|
if (paramStr) {
|
||||||
|
return `${url}?${paramStr}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PontCore = PontCoreManager.getSignleInstance();
|
BIN
src/assets/PDF.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
src/assets/WORD.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
src/assets/crun.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
src/assets/excel.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
src/assets/iam.png
Normal file
After Width: | Height: | Size: 210 KiB |
BIN
src/assets/image.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
src/assets/login-bg.png
Normal file
After Width: | Height: | Size: 297 KiB |
BIN
src/assets/logo.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
1
src/assets/vue.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
After Width: | Height: | Size: 496 B |
297
src/auto-imports.d.ts
vendored
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
// Generated by unplugin-auto-import
|
||||||
|
// biome-ignore lint: disable
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
const EffectScope: typeof import('vue')['EffectScope']
|
||||||
|
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
||||||
|
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
||||||
|
const computed: typeof import('vue')['computed']
|
||||||
|
const computedAsync: typeof import('@vueuse/core')['computedAsync']
|
||||||
|
const computedEager: typeof import('@vueuse/core')['computedEager']
|
||||||
|
const computedInject: typeof import('@vueuse/core')['computedInject']
|
||||||
|
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
|
||||||
|
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
|
||||||
|
const controlledRef: typeof import('@vueuse/core')['controlledRef']
|
||||||
|
const createApp: typeof import('vue')['createApp']
|
||||||
|
const createEventHook: typeof import('@vueuse/core')['createEventHook']
|
||||||
|
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
|
||||||
|
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
|
||||||
|
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
|
||||||
|
const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate']
|
||||||
|
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
|
||||||
|
const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise']
|
||||||
|
const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
|
||||||
|
const customRef: typeof import('vue')['customRef']
|
||||||
|
const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
|
||||||
|
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
|
||||||
|
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||||
|
const defineComponent: typeof import('vue')['defineComponent']
|
||||||
|
const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
|
||||||
|
const effectScope: typeof import('vue')['effectScope']
|
||||||
|
const extendRef: typeof import('@vueuse/core')['extendRef']
|
||||||
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||||
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||||
|
const h: typeof import('vue')['h']
|
||||||
|
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
|
||||||
|
const inject: typeof import('vue')['inject']
|
||||||
|
const injectLocal: typeof import('@vueuse/core')['injectLocal']
|
||||||
|
const isDefined: typeof import('@vueuse/core')['isDefined']
|
||||||
|
const isProxy: typeof import('vue')['isProxy']
|
||||||
|
const isReactive: typeof import('vue')['isReactive']
|
||||||
|
const isReadonly: typeof import('vue')['isReadonly']
|
||||||
|
const isRef: typeof import('vue')['isRef']
|
||||||
|
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
|
||||||
|
const markRaw: typeof import('vue')['markRaw']
|
||||||
|
const nextTick: typeof import('vue')['nextTick']
|
||||||
|
const onActivated: typeof import('vue')['onActivated']
|
||||||
|
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||||
|
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
|
||||||
|
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
|
||||||
|
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||||
|
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||||
|
const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
|
||||||
|
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||||
|
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||||
|
const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
|
||||||
|
const onLongPress: typeof import('@vueuse/core')['onLongPress']
|
||||||
|
const onMounted: typeof import('vue')['onMounted']
|
||||||
|
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||||
|
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||||
|
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||||
|
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||||
|
const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
|
||||||
|
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||||
|
const onUpdated: typeof import('vue')['onUpdated']
|
||||||
|
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
|
||||||
|
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
|
||||||
|
const provide: typeof import('vue')['provide']
|
||||||
|
const provideLocal: typeof import('@vueuse/core')['provideLocal']
|
||||||
|
const reactify: typeof import('@vueuse/core')['reactify']
|
||||||
|
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
|
||||||
|
const reactive: typeof import('vue')['reactive']
|
||||||
|
const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
|
||||||
|
const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
|
||||||
|
const reactivePick: typeof import('@vueuse/core')['reactivePick']
|
||||||
|
const readonly: typeof import('vue')['readonly']
|
||||||
|
const ref: typeof import('vue')['ref']
|
||||||
|
const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
|
||||||
|
const refDebounced: typeof import('@vueuse/core')['refDebounced']
|
||||||
|
const refDefault: typeof import('@vueuse/core')['refDefault']
|
||||||
|
const refThrottled: typeof import('@vueuse/core')['refThrottled']
|
||||||
|
const refWithControl: typeof import('@vueuse/core')['refWithControl']
|
||||||
|
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||||
|
const resolveRef: typeof import('@vueuse/core')['resolveRef']
|
||||||
|
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
|
||||||
|
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||||
|
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||||
|
const shallowRef: typeof import('vue')['shallowRef']
|
||||||
|
const syncRef: typeof import('@vueuse/core')['syncRef']
|
||||||
|
const syncRefs: typeof import('@vueuse/core')['syncRefs']
|
||||||
|
const templateRef: typeof import('@vueuse/core')['templateRef']
|
||||||
|
const throttledRef: typeof import('@vueuse/core')['throttledRef']
|
||||||
|
const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
|
||||||
|
const toRaw: typeof import('vue')['toRaw']
|
||||||
|
const toReactive: typeof import('@vueuse/core')['toReactive']
|
||||||
|
const toRef: typeof import('vue')['toRef']
|
||||||
|
const toRefs: typeof import('vue')['toRefs']
|
||||||
|
const toValue: typeof import('vue')['toValue']
|
||||||
|
const triggerRef: typeof import('vue')['triggerRef']
|
||||||
|
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
|
||||||
|
const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
|
||||||
|
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
|
||||||
|
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
|
||||||
|
const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
|
||||||
|
const unref: typeof import('vue')['unref']
|
||||||
|
const unrefElement: typeof import('@vueuse/core')['unrefElement']
|
||||||
|
const until: typeof import('@vueuse/core')['until']
|
||||||
|
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
|
||||||
|
const useAnimate: typeof import('@vueuse/core')['useAnimate']
|
||||||
|
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
|
||||||
|
const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery']
|
||||||
|
const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter']
|
||||||
|
const useArrayFind: typeof import('@vueuse/core')['useArrayFind']
|
||||||
|
const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex']
|
||||||
|
const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast']
|
||||||
|
const useArrayIncludes: typeof import('@vueuse/core')['useArrayIncludes']
|
||||||
|
const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin']
|
||||||
|
const useArrayMap: typeof import('@vueuse/core')['useArrayMap']
|
||||||
|
const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce']
|
||||||
|
const useArraySome: typeof import('@vueuse/core')['useArraySome']
|
||||||
|
const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique']
|
||||||
|
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
|
||||||
|
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
|
||||||
|
const useAttrs: typeof import('vue')['useAttrs']
|
||||||
|
const useBase64: typeof import('@vueuse/core')['useBase64']
|
||||||
|
const useBattery: typeof import('@vueuse/core')['useBattery']
|
||||||
|
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
|
||||||
|
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
|
||||||
|
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
|
||||||
|
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
|
||||||
|
const useCached: typeof import('@vueuse/core')['useCached']
|
||||||
|
const useClipboard: typeof import('@vueuse/core')['useClipboard']
|
||||||
|
const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
|
||||||
|
const useCloned: typeof import('@vueuse/core')['useCloned']
|
||||||
|
const useColorMode: typeof import('@vueuse/core')['useColorMode']
|
||||||
|
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
|
||||||
|
const useCounter: typeof import('@vueuse/core')['useCounter']
|
||||||
|
const useCssModule: typeof import('vue')['useCssModule']
|
||||||
|
const useCssVar: typeof import('@vueuse/core')['useCssVar']
|
||||||
|
const useCssVars: typeof import('vue')['useCssVars']
|
||||||
|
const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement']
|
||||||
|
const useCycleList: typeof import('@vueuse/core')['useCycleList']
|
||||||
|
const useDark: typeof import('@vueuse/core')['useDark']
|
||||||
|
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
|
||||||
|
const useDebounce: typeof import('@vueuse/core')['useDebounce']
|
||||||
|
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
|
||||||
|
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
|
||||||
|
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
|
||||||
|
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
|
||||||
|
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
|
||||||
|
const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
|
||||||
|
const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
|
||||||
|
const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
|
||||||
|
const useDraggable: typeof import('@vueuse/core')['useDraggable']
|
||||||
|
const useDropZone: typeof import('@vueuse/core')['useDropZone']
|
||||||
|
const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
|
||||||
|
const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
|
||||||
|
const useElementHover: typeof import('@vueuse/core')['useElementHover']
|
||||||
|
const useElementSize: typeof import('@vueuse/core')['useElementSize']
|
||||||
|
const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
|
||||||
|
const useEventBus: typeof import('@vueuse/core')['useEventBus']
|
||||||
|
const useEventListener: typeof import('@vueuse/core')['useEventListener']
|
||||||
|
const useEventSource: typeof import('@vueuse/core')['useEventSource']
|
||||||
|
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
|
||||||
|
const useFavicon: typeof import('@vueuse/core')['useFavicon']
|
||||||
|
const useFetch: typeof import('@vueuse/core')['useFetch']
|
||||||
|
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
|
||||||
|
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
|
||||||
|
const useFocus: typeof import('@vueuse/core')['useFocus']
|
||||||
|
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
|
||||||
|
const useFps: typeof import('@vueuse/core')['useFps']
|
||||||
|
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
|
||||||
|
const useGamepad: typeof import('@vueuse/core')['useGamepad']
|
||||||
|
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
|
||||||
|
const useId: typeof import('vue')['useId']
|
||||||
|
const useIdle: typeof import('@vueuse/core')['useIdle']
|
||||||
|
const useImage: typeof import('@vueuse/core')['useImage']
|
||||||
|
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
|
||||||
|
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
|
||||||
|
const useInterval: typeof import('@vueuse/core')['useInterval']
|
||||||
|
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
|
||||||
|
const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
|
||||||
|
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
|
||||||
|
const useLink: typeof import('vue-router')['useLink']
|
||||||
|
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
|
||||||
|
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
|
||||||
|
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
|
||||||
|
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
|
||||||
|
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
|
||||||
|
const useMemoize: typeof import('@vueuse/core')['useMemoize']
|
||||||
|
const useMemory: typeof import('@vueuse/core')['useMemory']
|
||||||
|
const useModel: typeof import('vue')['useModel']
|
||||||
|
const useMounted: typeof import('@vueuse/core')['useMounted']
|
||||||
|
const useMouse: typeof import('@vueuse/core')['useMouse']
|
||||||
|
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
|
||||||
|
const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
|
||||||
|
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
|
||||||
|
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
|
||||||
|
const useNetwork: typeof import('@vueuse/core')['useNetwork']
|
||||||
|
const useNow: typeof import('@vueuse/core')['useNow']
|
||||||
|
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
|
||||||
|
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
|
||||||
|
const useOnline: typeof import('@vueuse/core')['useOnline']
|
||||||
|
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
|
||||||
|
const useParallax: typeof import('@vueuse/core')['useParallax']
|
||||||
|
const useParentElement: typeof import('@vueuse/core')['useParentElement']
|
||||||
|
const usePerformanceObserver: typeof import('@vueuse/core')['usePerformanceObserver']
|
||||||
|
const usePermission: typeof import('@vueuse/core')['usePermission']
|
||||||
|
const usePointer: typeof import('@vueuse/core')['usePointer']
|
||||||
|
const usePointerLock: typeof import('@vueuse/core')['usePointerLock']
|
||||||
|
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
|
||||||
|
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
|
||||||
|
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
|
||||||
|
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
|
||||||
|
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
|
||||||
|
const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion']
|
||||||
|
const usePrevious: typeof import('@vueuse/core')['usePrevious']
|
||||||
|
const useRafFn: typeof import('@vueuse/core')['useRafFn']
|
||||||
|
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
|
||||||
|
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
|
||||||
|
const useRoute: typeof import('vue-router')['useRoute']
|
||||||
|
const useRouter: typeof import('vue-router')['useRouter']
|
||||||
|
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
|
||||||
|
const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
|
||||||
|
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
|
||||||
|
const useScroll: typeof import('@vueuse/core')['useScroll']
|
||||||
|
const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
|
||||||
|
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
|
||||||
|
const useShare: typeof import('@vueuse/core')['useShare']
|
||||||
|
const useSlots: typeof import('vue')['useSlots']
|
||||||
|
const useSorted: typeof import('@vueuse/core')['useSorted']
|
||||||
|
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
|
||||||
|
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
|
||||||
|
const useStepper: typeof import('@vueuse/core')['useStepper']
|
||||||
|
const useStorage: typeof import('@vueuse/core')['useStorage']
|
||||||
|
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
|
||||||
|
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
|
||||||
|
const useSupported: typeof import('@vueuse/core')['useSupported']
|
||||||
|
const useSwipe: typeof import('@vueuse/core')['useSwipe']
|
||||||
|
const useTemplateRef: typeof import('vue')['useTemplateRef']
|
||||||
|
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
|
||||||
|
const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
|
||||||
|
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
|
||||||
|
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
|
||||||
|
const useThrottle: typeof import('@vueuse/core')['useThrottle']
|
||||||
|
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
|
||||||
|
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
|
||||||
|
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
|
||||||
|
const useTimeout: typeof import('@vueuse/core')['useTimeout']
|
||||||
|
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
|
||||||
|
const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
|
||||||
|
const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
|
||||||
|
const useTitle: typeof import('@vueuse/core')['useTitle']
|
||||||
|
const useToNumber: typeof import('@vueuse/core')['useToNumber']
|
||||||
|
const useToString: typeof import('@vueuse/core')['useToString']
|
||||||
|
const useToggle: typeof import('@vueuse/core')['useToggle']
|
||||||
|
const useTransition: typeof import('@vueuse/core')['useTransition']
|
||||||
|
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
|
||||||
|
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
|
||||||
|
const useVModel: typeof import('@vueuse/core')['useVModel']
|
||||||
|
const useVModels: typeof import('@vueuse/core')['useVModels']
|
||||||
|
const useVibrate: typeof import('@vueuse/core')['useVibrate']
|
||||||
|
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
|
||||||
|
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
|
||||||
|
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
|
||||||
|
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
|
||||||
|
const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
|
||||||
|
const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
|
||||||
|
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
|
||||||
|
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
|
||||||
|
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
|
||||||
|
const watch: typeof import('vue')['watch']
|
||||||
|
const watchArray: typeof import('@vueuse/core')['watchArray']
|
||||||
|
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
|
||||||
|
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
|
||||||
|
const watchDeep: typeof import('@vueuse/core')['watchDeep']
|
||||||
|
const watchEffect: typeof import('vue')['watchEffect']
|
||||||
|
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
|
||||||
|
const watchImmediate: typeof import('@vueuse/core')['watchImmediate']
|
||||||
|
const watchOnce: typeof import('@vueuse/core')['watchOnce']
|
||||||
|
const watchPausable: typeof import('@vueuse/core')['watchPausable']
|
||||||
|
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||||
|
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||||
|
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
|
||||||
|
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
|
||||||
|
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
|
||||||
|
const whenever: typeof import('@vueuse/core')['whenever']
|
||||||
|
}
|
||||||
|
// for type re-export
|
||||||
|
declare global {
|
||||||
|
// @ts-ignore
|
||||||
|
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||||
|
import('vue')
|
||||||
|
}
|
99
src/components.d.ts
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by unplugin-vue-components
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
export {}
|
||||||
|
|
||||||
|
/* prettier-ignore */
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
AAlert: typeof import('ant-design-vue/es')['Alert']
|
||||||
|
AAvatar: typeof import('ant-design-vue/es')['Avatar']
|
||||||
|
ABadge: typeof import('ant-design-vue/es')['Badge']
|
||||||
|
ABadgeRibbon: typeof import('ant-design-vue/es')['BadgeRibbon']
|
||||||
|
AButton: typeof import('ant-design-vue/es')['Button']
|
||||||
|
ACard: typeof import('ant-design-vue/es')['Card']
|
||||||
|
ACardGrid: typeof import('ant-design-vue/es')['CardGrid']
|
||||||
|
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
|
||||||
|
ACol: typeof import('ant-design-vue/es')['Col']
|
||||||
|
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
|
||||||
|
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
|
||||||
|
ADescriptions: typeof import('ant-design-vue/es')['Descriptions']
|
||||||
|
ADescriptionsItem: typeof import('ant-design-vue/es')['DescriptionsItem']
|
||||||
|
ADivider: typeof import('ant-design-vue/es')['Divider']
|
||||||
|
ADrawer: typeof import('ant-design-vue/es')['Drawer']
|
||||||
|
ADropdown: typeof import('ant-design-vue/es')['Dropdown']
|
||||||
|
AFlex: typeof import('ant-design-vue/es')['Flex']
|
||||||
|
AFloatButton: typeof import('ant-design-vue/es')['FloatButton']
|
||||||
|
AForm: typeof import('ant-design-vue/es')['Form']
|
||||||
|
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
||||||
|
AImage: typeof import('ant-design-vue/es')['Image']
|
||||||
|
AInput: typeof import('ant-design-vue/es')['Input']
|
||||||
|
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||||
|
AInputSearch: typeof import('ant-design-vue/es')['InputSearch']
|
||||||
|
ALayout: typeof import('ant-design-vue/es')['Layout']
|
||||||
|
ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']
|
||||||
|
ALayoutFooter: typeof import('ant-design-vue/es')['LayoutFooter']
|
||||||
|
ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader']
|
||||||
|
ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider']
|
||||||
|
AMentionsOption: typeof import('ant-design-vue/es')['MentionsOption']
|
||||||
|
AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||||
|
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
||||||
|
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||||
|
APageHeader: typeof import('ant-design-vue/es')['PageHeader']
|
||||||
|
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||||
|
ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
|
||||||
|
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
||||||
|
AResult: typeof import('ant-design-vue/es')['Result']
|
||||||
|
ARow: typeof import('ant-design-vue/es')['Row']
|
||||||
|
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||||
|
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
||||||
|
ASlider: typeof import('ant-design-vue/es')['Slider']
|
||||||
|
ASpace: typeof import('ant-design-vue/es')['Space']
|
||||||
|
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||||
|
AStatistic: typeof import('ant-design-vue/es')['Statistic']
|
||||||
|
ASteps: typeof import('ant-design-vue/es')['Steps']
|
||||||
|
ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
|
||||||
|
ASwitch: typeof import('ant-design-vue/es')['Switch']
|
||||||
|
ATable: typeof import('ant-design-vue/es')['Table']
|
||||||
|
ATabPane: typeof import('ant-design-vue/es')['TabPane']
|
||||||
|
ATabs: typeof import('ant-design-vue/es')['Tabs']
|
||||||
|
ATag: typeof import('ant-design-vue/es')['Tag']
|
||||||
|
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
||||||
|
ATransfer: typeof import('ant-design-vue/es')['Transfer']
|
||||||
|
ATree: typeof import('ant-design-vue/es')['Tree']
|
||||||
|
ATypographyLink: typeof import('ant-design-vue/es')['TypographyLink']
|
||||||
|
ATypographyParagraph: typeof import('ant-design-vue/es')['TypographyParagraph']
|
||||||
|
ATypographyTitle: typeof import('ant-design-vue/es')['TypographyTitle']
|
||||||
|
AuxiliaryItemRender: typeof import('./components/form-render/auxiliary-item-render.vue')['default']
|
||||||
|
AWatermark: typeof import('ant-design-vue/es')['Watermark']
|
||||||
|
CodeGeneratorDrawer: typeof import('./components/form-designer/component-container/code-generator-drawer.vue')['default']
|
||||||
|
ColComponentItem: typeof import('./components/form-designer/component-container/col-component-item.vue')['default']
|
||||||
|
ComponentContainer: typeof import('./components/form-designer/component-container/component-container.vue')['default']
|
||||||
|
ComponentItem: typeof import('./components/form-designer/component-container/component-item.vue')['default']
|
||||||
|
ComponentPanel: typeof import('./components/form-designer/component-panel/component-panel.vue')['default']
|
||||||
|
ConfigPanel: typeof import('./components/form-designer/config-panel/config-panel.vue')['default']
|
||||||
|
DataSourceTable: typeof import('./components/form-designer/config-panel/data-source-table.vue')['default']
|
||||||
|
FormConfigPanel: typeof import('./components/form-designer/config-panel/form-config-panel.vue')['default']
|
||||||
|
FormDesigner: typeof import('./components/form-designer/form-designer.vue')['default']
|
||||||
|
FormDrawer: typeof import('./components/form-render/form-drawer.vue')['default']
|
||||||
|
FormItemConfigPanel: typeof import('./components/form-designer/config-panel/form-item-config-panel.vue')['default']
|
||||||
|
FormItemRender: typeof import('./components/form-render/form-item-render.vue')['default']
|
||||||
|
FormModal: typeof import('./components/form-render/form-modal.vue')['default']
|
||||||
|
FormPreviewDrawer: typeof import('./components/form-designer/component-container/form-preview-drawer.vue')['default']
|
||||||
|
FormRender: typeof import('./components/form-render/form-render.vue')['default']
|
||||||
|
IconFont: typeof import('./components/icon-font/icon-font.vue')['default']
|
||||||
|
ImageUploader: typeof import('./components/image-uploader/image-uploader.vue')['default']
|
||||||
|
InputItem: typeof import('./components/form-designer/component-panel/input-item.vue')['default']
|
||||||
|
OptionTable: typeof import('./components/form-designer/config-panel/option-table.vue')['default']
|
||||||
|
PageContainer: typeof import('./components/page-container/page-container.vue')['default']
|
||||||
|
PropertyConfig: typeof import('./components/form-designer/config-panel/property-config.vue')['default']
|
||||||
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
RowComponentItem: typeof import('./components/form-designer/component-container/row-component-item.vue')['default']
|
||||||
|
SearchTree: typeof import('./components/search-tree.vue')['default']
|
||||||
|
SelectedTree: typeof import('./components/search-tree.vue')['default']
|
||||||
|
TreeDataTable: typeof import('./components/form-designer/config-panel/tree-data-table.vue')['default']
|
||||||
|
ValidateRuleConfig: typeof import('./components/form-designer/config-panel/validate-rule-config.vue')['default']
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,253 @@
|
|||||||
|
<template>
|
||||||
|
<a-drawer v-model:open="visible" title="生成结果" width="50%" :closable="false">
|
||||||
|
<a-tabs v-model:active-key="activeKey">
|
||||||
|
<template #rightExtra>
|
||||||
|
<a-button type="primary" size="small" @click="copy(code)">
|
||||||
|
<template #icon>
|
||||||
|
<icon-font type="icon-copy"></icon-font>
|
||||||
|
</template>
|
||||||
|
复制
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<a-tab-pane key="form" tab="form.ts"></a-tab-pane>
|
||||||
|
<a-tab-pane key="render" tab="form.vue"></a-tab-pane>
|
||||||
|
<a-tab-pane key="modal" tab="modal.vue"></a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
<codemirror
|
||||||
|
v-model="code"
|
||||||
|
disabled
|
||||||
|
:style="{ height: 'calc(100vh - 175px)' }"
|
||||||
|
:autofocus="true"
|
||||||
|
:indent-with-tab="true"
|
||||||
|
:tab-size="2"
|
||||||
|
:extensions="extensions"
|
||||||
|
/>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { PropType, computed, ref, toRefs, watch } from 'vue'
|
||||||
|
import { Codemirror } from 'vue-codemirror'
|
||||||
|
import { java } from '@codemirror/lang-java'
|
||||||
|
import { javascript } from '@codemirror/lang-javascript'
|
||||||
|
import { oneDark } from '@codemirror/theme-one-dark'
|
||||||
|
import XEUtils from 'xe-utils'
|
||||||
|
import { notification } from 'ant-design-vue'
|
||||||
|
import { useClipboard } from '@vueuse/core'
|
||||||
|
import { FormDesignerItem } from '../form-designer-types'
|
||||||
|
import { FormConfig, FormItem } from '@/components/form-render/form-render-types'
|
||||||
|
|
||||||
|
const extensions = [java(), javascript({ jsx: true, typescript: true }), oneDark]
|
||||||
|
const visible = ref(false)
|
||||||
|
|
||||||
|
const show = () => {
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
show,
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
components: {
|
||||||
|
type: Array as PropType<FormDesignerItem[]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
type: Object as PropType<FormConfig>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const { components, config } = toRefs(props)
|
||||||
|
|
||||||
|
const hanldConfig = (item: FormDesignerItem) => {
|
||||||
|
if (item.formItem.config) {
|
||||||
|
delete item.formItem.config.labelColFixedWidthInPx
|
||||||
|
delete item.formItem.config.labelColSpanWidth
|
||||||
|
delete item.formItem.config.labelType
|
||||||
|
delete item.formItem.config.useFormConfig
|
||||||
|
if (item.formItem.config.labelType === 'fixed') {
|
||||||
|
delete item.formItem.config.labelCol?.span
|
||||||
|
delete item.formItem.config.labelCol?.offset
|
||||||
|
} else {
|
||||||
|
delete item.formItem.config.labelCol?.style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.formItem.properties.options) {
|
||||||
|
item.formItem.properties.options.forEach((item) => {
|
||||||
|
delete item.uuid
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (item.formItem.rules?.length) {
|
||||||
|
item.formItem.rules.forEach((item) => {
|
||||||
|
;(item as { uuid?: string }).uuid = undefined
|
||||||
|
if (item.enum) {
|
||||||
|
item.enum = String(item.enum).split(',')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const activeKey = ref('form')
|
||||||
|
const configTransfer = (item: FormDesignerItem): FormItem => {
|
||||||
|
if (item.formItem) {
|
||||||
|
delete item.formItem.propertyConfigFormComponents
|
||||||
|
}
|
||||||
|
if (item.formItem?.config) {
|
||||||
|
hanldConfig(item)
|
||||||
|
}
|
||||||
|
if (item.formItem.propertiesTransfer) {
|
||||||
|
item.formItem.properties = item.formItem.propertiesTransfer(item.formItem.properties)
|
||||||
|
}
|
||||||
|
//递归一下
|
||||||
|
if (item.children) {
|
||||||
|
return XEUtils.assign(item.formItem, { children: item.children.map((item) => configTransfer(item)) })
|
||||||
|
}
|
||||||
|
return item.formItem
|
||||||
|
}
|
||||||
|
const code = computed(() => {
|
||||||
|
const formConfig = XEUtils.clone(config.value, true)
|
||||||
|
if (formConfig.labelType === 'fixed') {
|
||||||
|
delete formConfig.labelCol?.span
|
||||||
|
delete formConfig.labelCol?.offset
|
||||||
|
} else {
|
||||||
|
delete formConfig.labelCol?.style
|
||||||
|
}
|
||||||
|
delete formConfig.labelColFixedWidthInPx
|
||||||
|
delete formConfig.labelColSpanWidth
|
||||||
|
delete formConfig.labelType
|
||||||
|
switch (activeKey.value) {
|
||||||
|
case 'form':
|
||||||
|
return `import { FormItem, FormConfig } from '@/components/form-render/form-render-types';
|
||||||
|
|
||||||
|
export const config: FormConfig = ${JSON.stringify(formConfig, undefined, 2)}
|
||||||
|
|
||||||
|
export const formItems: FormItem[] = ${JSON.stringify(
|
||||||
|
XEUtils.clone(components.value, true).map((item) => configTransfer(item)),
|
||||||
|
undefined,
|
||||||
|
2,
|
||||||
|
)}
|
||||||
|
`
|
||||||
|
case 'modal':
|
||||||
|
return `<template>
|
||||||
|
<div>
|
||||||
|
<a-button type="primary" @click="add">添加</a-button>
|
||||||
|
<form-modal
|
||||||
|
ref="formModal"
|
||||||
|
v-model="data"
|
||||||
|
:form-items="items"
|
||||||
|
:config="formConfig"
|
||||||
|
:title="title"
|
||||||
|
:hidden-fields="hiddenFields"
|
||||||
|
:disabled-fields="disabledFields"
|
||||||
|
@ok="hanldOk"
|
||||||
|
></form-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import FormModal from '@/components/form-render/form-modal.vue';
|
||||||
|
import { config, formItems } from './form';
|
||||||
|
import { FormDataType } from '@/components/form-render/form-render-types';
|
||||||
|
const items = computed(() => {
|
||||||
|
return formItems;
|
||||||
|
});
|
||||||
|
|
||||||
|
const formConfig = computed(() => {
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = ref<FormDataType>({});
|
||||||
|
const title = computed(() => {
|
||||||
|
//TODO 根据数据情况设置标题
|
||||||
|
return '添加';
|
||||||
|
});
|
||||||
|
const hiddenFields = computed(() => {
|
||||||
|
//TODO 根据数据情况设置隐藏字段
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
const hanldOk = (_data: FormDataType) => {
|
||||||
|
//TODO 根据业务情况处理
|
||||||
|
console.log(_data);
|
||||||
|
formModal.value?.close(); //关闭弹窗
|
||||||
|
};
|
||||||
|
const disabledFields = computed(() => {
|
||||||
|
//TODO 根据数据情况设置隐藏字段
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
const formModal = ref<typeof FormModal>();
|
||||||
|
|
||||||
|
const add = () => {
|
||||||
|
formModal.value?.open(); //通过ref可调用的其他方法有 open, close,reset, validateFields,resetFields,getModalContentWrapper, getPopupContainerId, closeLoading,
|
||||||
|
};
|
||||||
|
// 其他属性包含 maxHeight,width,wrapClassName,modalContentWrapperClassName
|
||||||
|
<\\/script>
|
||||||
|
`.replace('\\', '')
|
||||||
|
case 'render':
|
||||||
|
return `<template>
|
||||||
|
<form-render
|
||||||
|
v-model="data"
|
||||||
|
:items="items"
|
||||||
|
:config="formConfig"
|
||||||
|
:hidden-fields="hiddenFields"
|
||||||
|
:disabled-fields="disabledFields"
|
||||||
|
></form-render>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { config, formItems } from './form';
|
||||||
|
import { FormDataType } from '@/components/form-render/form-render-types';
|
||||||
|
const items = computed(() => {
|
||||||
|
return formItems;
|
||||||
|
});
|
||||||
|
|
||||||
|
const formConfig = computed(() => {
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = ref<FormDataType>({});
|
||||||
|
const hiddenFields = computed(() => {
|
||||||
|
//TODO 根据数据情况设置隐藏字段
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
const disabledFields = computed(() => {
|
||||||
|
//TODO 根据数据情况设置隐藏字段
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
// 可通过ref调用render的方法有 validateFields, resetFields, clearValidate
|
||||||
|
<\\/script>
|
||||||
|
`.replace('\\', '')
|
||||||
|
default:
|
||||||
|
return `
|
||||||
|
<template>
|
||||||
|
<form-render v-model="formData" :form-config="formConfig" :form-items="formItems" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import FormRender from '@/components/form-render/form-render.vue';
|
||||||
|
import { useFormItems } from './form-items';
|
||||||
|
import type { FormConfig } from '@/components/form-render/types/form-designer';
|
||||||
|
import type { FormData } from '@/components/form-render/types/form-render';
|
||||||
|
|
||||||
|
const formData = ref<FormData>({
|
||||||
|
layout: 'horizontal',
|
||||||
|
colon: true,
|
||||||
|
hideRequiredMark: false,
|
||||||
|
labelAlign: 'right',
|
||||||
|
labelType: 'fixed',
|
||||||
|
scrollToFirstError: false,
|
||||||
|
validateOnRuleChange: true,
|
||||||
|
labelCol: {}
|
||||||
|
});
|
||||||
|
const formConfig = ref<FormConfig>();
|
||||||
|
const { formItems } = useFormItems();
|
||||||
|
<\\/script>
|
||||||
|
`.replace('\\', '')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const { copy, text } = useClipboard()
|
||||||
|
|
||||||
|
watch(text, (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
notification.success({
|
||||||
|
message: '操作成功',
|
||||||
|
description: '代码已经成功复制',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
@ -0,0 +1,119 @@
|
|||||||
|
<template>
|
||||||
|
<a-col :id="id" v-bind="Object.assign({}, $attrs, item.formItem.properties)" :data-id="item.uuid">
|
||||||
|
<component-item
|
||||||
|
v-for="(c, index) in item.children"
|
||||||
|
:key="c.uuid"
|
||||||
|
:data-id="c.uuid"
|
||||||
|
:item="c"
|
||||||
|
:selected="selected"
|
||||||
|
@selected="selectedChange"
|
||||||
|
@copy="copyItem(index)"
|
||||||
|
@delete="deleteItem(index, $event)"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { FormDesignerItem } from '../form-designer-types'
|
||||||
|
import Sortable from 'sortablejs'
|
||||||
|
import allComponents from '../component-panel/components'
|
||||||
|
import XEUtils from 'xe-utils'
|
||||||
|
import { notification } from 'ant-design-vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
item: {
|
||||||
|
type: Object as PropType<FormDesignerItem>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
type: Object as PropType<FormDesignerItem>,
|
||||||
|
required: false,
|
||||||
|
default: () => null,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
type: Array as PropType<Array<FormDesignerItem>>,
|
||||||
|
required: false,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const { item, selected, components } = toRefs(props)
|
||||||
|
|
||||||
|
// 复制表单项
|
||||||
|
const copyItem = (index: number) => {
|
||||||
|
if (item.value.children) {
|
||||||
|
const copyItem = XEUtils.assign(XEUtils.clone(item.value.children[index], true), { uuid: XEUtils.uniqueId() })
|
||||||
|
if (copyItem.formItem.config?.name)
|
||||||
|
copyItem.formItem.config.name = copyItem.formItem.config.name.split('_')[0] + '_' + XEUtils.uniqueId()
|
||||||
|
// 必须深拷贝,否则对象类型的字段会拷贝引用
|
||||||
|
item.value.children.splice(index + 1, 0, copyItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除表单项
|
||||||
|
const deleteItem = (index: number, _item: FormDesignerItem) => {
|
||||||
|
if (item.value.children) {
|
||||||
|
item.value.children.splice(index, 1)
|
||||||
|
console.log(_item) //需要判定删除的组件是不是选中的组件
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
selectedChange: [config?: FormDesignerItem]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const selectedChange = (_config?: FormDesignerItem) => {
|
||||||
|
emit('selectedChange', _config)
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = computed(() => {
|
||||||
|
return `drag-col-${item.value.uuid}`
|
||||||
|
})
|
||||||
|
nextTick(() => {
|
||||||
|
const dom = document.getElementById(id.value)
|
||||||
|
if (dom) {
|
||||||
|
Sortable.create(dom, {
|
||||||
|
animation: 150,
|
||||||
|
sort: true,
|
||||||
|
group: { name: 'components', pull: true, put: true },
|
||||||
|
onEnd: (evt) => {
|
||||||
|
item.value.children = [...evt.from.children]
|
||||||
|
.filter((c) => (c as HTMLElement).style.display !== 'none')
|
||||||
|
.map((c) => {
|
||||||
|
const id = (c as HTMLElement).dataset.id
|
||||||
|
return (item.value.children ?? []).find((c) => c.uuid === id)
|
||||||
|
}) as FormDesignerItem[]
|
||||||
|
},
|
||||||
|
onAdd: (evt) => {
|
||||||
|
;[...evt.to.children]
|
||||||
|
.filter((item) => item.classList.contains('ant-card-grid'))
|
||||||
|
.forEach((item) => {
|
||||||
|
;(item as HTMLElement).style.display = 'none'
|
||||||
|
}) //原始元素都别给我显示出来了
|
||||||
|
const uuid = evt.clone.dataset.id
|
||||||
|
const type = evt.clone.dataset.type
|
||||||
|
const newItem = type
|
||||||
|
? allComponents.find((item) => item.type === type)
|
||||||
|
: XEUtils.findTree(components.value, (item) => item.uuid === uuid).item
|
||||||
|
|
||||||
|
if (newItem?.formItem.group === 'layout') {
|
||||||
|
notification.warning({
|
||||||
|
message: '提示',
|
||||||
|
description: '暂不支持布局嵌套,请拖入表单组件',
|
||||||
|
duration: 3,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (newItem) {
|
||||||
|
item.value.children?.push(XEUtils.assign(XEUtils.clone(newItem, true), { uuid: XEUtils.uniqueId() }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.ant-col,
|
||||||
|
.ant-row {
|
||||||
|
min-height: 50px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,267 @@
|
|||||||
|
<template>
|
||||||
|
<a-card size="small">
|
||||||
|
<template #title>
|
||||||
|
<a-tabs centered size="small">
|
||||||
|
<a-tab-pane key="form" tab="预览区" />
|
||||||
|
</a-tabs>
|
||||||
|
</template>
|
||||||
|
<template #extra>
|
||||||
|
<a-space v-if="notEmpty">
|
||||||
|
<a-button size="small" type="primary" @click="generate">
|
||||||
|
<template #icon>
|
||||||
|
<icon-font type="icon-code" />
|
||||||
|
</template>
|
||||||
|
生成
|
||||||
|
</a-button>
|
||||||
|
<a-button size="small" type="primary" @click="preview">
|
||||||
|
<template #icon>
|
||||||
|
<icon-font type="icon-preview" />
|
||||||
|
</template>
|
||||||
|
预览
|
||||||
|
</a-button>
|
||||||
|
<a-popconfirm title="确定清空设计?" ok-text="确定" cancel-text="取消" @confirm="reset">
|
||||||
|
<a-button size="small" type="primary" danger>
|
||||||
|
<template #icon>
|
||||||
|
<icon-font type="icon-reset" />
|
||||||
|
</template>
|
||||||
|
清空
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<div :style="{ minHeight: 'calc(100vh - 190px)', maxHeight: 'calc(100vh - 190px)', overflowY: 'auto' }">
|
||||||
|
<a-watermark :content="waterMark" :rotate="22" :font="{ color: 'rgba(0,0,0,.3)', fontSize: 20 }">
|
||||||
|
<a-form
|
||||||
|
v-bind="formConfig"
|
||||||
|
ref="formRef"
|
||||||
|
:style="{ '--ant-primary-color': token.colorPrimaryBorder }"
|
||||||
|
@click="selectedChange()"
|
||||||
|
>
|
||||||
|
<component-item
|
||||||
|
v-for="(c, index) in components"
|
||||||
|
:key="c.uuid"
|
||||||
|
:data-id="c.uuid"
|
||||||
|
:item="c"
|
||||||
|
:components="components"
|
||||||
|
:disabled-fields="disabledFields"
|
||||||
|
:selected="selected"
|
||||||
|
@selected="selectedChange"
|
||||||
|
@copy="copyItem(index)"
|
||||||
|
@delete="deleteItem(index, $event)"
|
||||||
|
/>
|
||||||
|
</a-form>
|
||||||
|
</a-watermark>
|
||||||
|
</div>
|
||||||
|
<code-generator-drawer
|
||||||
|
ref="codeGeneratorDrawer"
|
||||||
|
:components="components"
|
||||||
|
:config="formConfig"
|
||||||
|
></code-generator-drawer>
|
||||||
|
<form-preview-drawer ref="formPreviewModal" :components="components" :config="formConfig"></form-preview-drawer>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { FormDesignerItem } from '../form-designer-types'
|
||||||
|
import Sortable from 'sortablejs'
|
||||||
|
import allComponents from '../component-panel/components'
|
||||||
|
import { FormConfig, FormItem } from '@/components/form-render/form-render-types'
|
||||||
|
import XEUtils from 'xe-utils'
|
||||||
|
import { notification, theme } from 'ant-design-vue'
|
||||||
|
import FormPreviewDrawer from './form-preview-drawer.vue'
|
||||||
|
import CodeGeneratorDrawer from './code-generator-drawer.vue'
|
||||||
|
const { useToken } = theme
|
||||||
|
const { token } = useToken()
|
||||||
|
const props = defineProps({
|
||||||
|
formConfig: {
|
||||||
|
type: Object as PropType<FormConfig>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
// 选中的组件
|
||||||
|
selected: {
|
||||||
|
type: Object as PropType<FormDesignerItem>,
|
||||||
|
required: false,
|
||||||
|
default: () => {
|
||||||
|
return undefined
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 禁用的表单项
|
||||||
|
disabledFields: {
|
||||||
|
type: Array as PropType<Array<string>>,
|
||||||
|
required: false,
|
||||||
|
default: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isGenerate: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const { selected } = toRefs(props)
|
||||||
|
|
||||||
|
const selectedKey = computed(() => {
|
||||||
|
return selected?.value?.uuid
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
selectedChange: [config?: FormDesignerItem]
|
||||||
|
'update:form-items': [formItems: Array<FormItem>]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const selectedChange = (config?: FormDesignerItem) => {
|
||||||
|
emit('selectedChange', config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制表单项
|
||||||
|
const copyItem = (index: number) => {
|
||||||
|
const item = XEUtils.assign(XEUtils.clone(components.value[index], true), { uuid: XEUtils.uniqueId() })
|
||||||
|
if (item.formItem.config?.name)
|
||||||
|
item.formItem.config.name = item.formItem.config.name.split('_')[0] + '_' + XEUtils.uniqueId()
|
||||||
|
// 必须深拷贝,否则对象类型的字段会拷贝引用
|
||||||
|
components.value.splice(index + 1, 0, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除表单项
|
||||||
|
const deleteItem = (index: number, item: FormDesignerItem) => {
|
||||||
|
components.value.splice(index, 1)
|
||||||
|
if (item.uuid == selectedKey.value) {
|
||||||
|
// 如果删除的是当前选中的组件,则要清理选中组件
|
||||||
|
selectedChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const components = ref<Array<FormDesignerItem>>([])
|
||||||
|
const notEmpty = computed<boolean>(() => {
|
||||||
|
return components.value.length > 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const waterMark = computed<string>(() => {
|
||||||
|
return notEmpty.value ? ' ' : '拖动组件到这里'
|
||||||
|
})
|
||||||
|
const codeGeneratorDrawer = ref<typeof CodeGeneratorDrawer>()
|
||||||
|
// 生成
|
||||||
|
const generate = () => {
|
||||||
|
codeGeneratorDrawer.value?.show()
|
||||||
|
}
|
||||||
|
const formPreviewModal = ref<typeof FormPreviewDrawer>()
|
||||||
|
// 预览
|
||||||
|
const preview = () => {
|
||||||
|
formPreviewModal.value?.show()
|
||||||
|
}
|
||||||
|
// 清空
|
||||||
|
const reset = () => {
|
||||||
|
// 清空容器中的组件
|
||||||
|
components.value = []
|
||||||
|
selectedChange()
|
||||||
|
}
|
||||||
|
const sort = ref<Sortable>()
|
||||||
|
nextTick(() => {
|
||||||
|
const form = document.querySelector('.ant-form')
|
||||||
|
// 整体的组件
|
||||||
|
if (form) {
|
||||||
|
sort.value = Sortable.create(form as HTMLElement, {
|
||||||
|
animation: 150,
|
||||||
|
sort: true,
|
||||||
|
group: { name: 'components', pull: true, put: true },
|
||||||
|
onEnd: (evt) => {
|
||||||
|
components.value = [...evt.from.children]
|
||||||
|
.filter((item) => (item as HTMLElement).style.display !== 'none')
|
||||||
|
.map((item) => {
|
||||||
|
const id = (item as HTMLElement).dataset.id
|
||||||
|
return components.value.find((item) => item.uuid === id)
|
||||||
|
}) as FormDesignerItem[]
|
||||||
|
},
|
||||||
|
onAdd: (evt) => {
|
||||||
|
;[...evt.to.children]
|
||||||
|
.filter((item) => item.classList.contains('ant-card-grid'))
|
||||||
|
.forEach((item) => {
|
||||||
|
;(item as HTMLElement).style.display = 'none'
|
||||||
|
})
|
||||||
|
const uuid = evt.clone.dataset.id
|
||||||
|
const type = evt.clone.dataset.type
|
||||||
|
if (type === 'col') {
|
||||||
|
notification.warning({
|
||||||
|
message: '提示',
|
||||||
|
description: '列组件需要放入行组件',
|
||||||
|
duration: 3,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const item = type
|
||||||
|
? allComponents.find((item) => item.type === type)
|
||||||
|
: XEUtils.findTree(components.value, (item) => item.uuid === uuid).item
|
||||||
|
if (item) {
|
||||||
|
components.value.push(XEUtils.assign(XEUtils.clone(item, true), { uuid: XEUtils.uniqueId() }))
|
||||||
|
}
|
||||||
|
nextTick(() => {
|
||||||
|
rowDrag()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const rowDrag = () => {
|
||||||
|
;[...document.querySelectorAll('.list-group-item .drag-row')].forEach((item) => {
|
||||||
|
Sortable.create(item as HTMLElement, {
|
||||||
|
animation: 150,
|
||||||
|
sort: true,
|
||||||
|
group: { name: 'components', pull: true, put: true },
|
||||||
|
onEnd: (evt) => {
|
||||||
|
const item = components.value.find((a) => a.uuid === evt.to.dataset.id)
|
||||||
|
if (item) {
|
||||||
|
item.children = [...evt.from.children]
|
||||||
|
.filter((c) => (c as HTMLElement).style.display !== 'none')
|
||||||
|
.map((c) => {
|
||||||
|
const id = (c as HTMLElement).dataset.id
|
||||||
|
return (item.children ?? []).find((c) => c.uuid === id)
|
||||||
|
}) as FormDesignerItem[]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onAdd: (evt) => {
|
||||||
|
;[...evt.to.children]
|
||||||
|
.filter((item) => item.classList.contains('ant-card-grid'))
|
||||||
|
.forEach((item) => {
|
||||||
|
;(item as HTMLElement).style.display = 'none'
|
||||||
|
}) //原始元素都别给我显示出来了
|
||||||
|
|
||||||
|
//获取拖到了哪个行,添加拖入的列
|
||||||
|
const item = components.value.find((a) => a.uuid === evt.to.dataset.id)
|
||||||
|
const type = evt.clone.dataset.type
|
||||||
|
if (!type) {
|
||||||
|
notification.warning({
|
||||||
|
message: '提示',
|
||||||
|
description: '已设置组件放入布局将无法恢复!',
|
||||||
|
duration: 3,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (type !== 'col') {
|
||||||
|
notification.warning({
|
||||||
|
message: '提示',
|
||||||
|
description: '行组件只接受放入列组件',
|
||||||
|
duration: 3,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const newItem = allComponents.find((item) => item.type === type) //仅仅找到原始组件
|
||||||
|
if (newItem) {
|
||||||
|
item?.children?.push(XEUtils.assign(XEUtils.clone(newItem, true), { uuid: XEUtils.uniqueId() }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.ant-form {
|
||||||
|
height: calc(100vh - 190px);
|
||||||
|
padding: 15px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px dashed var(--ant-primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-pro-pro-layout-watermark-wrapper {
|
||||||
|
min-height: calc(100vh - 190px) !important;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<a-badge-ribbon color="volcano">
|
||||||
|
<template #text>
|
||||||
|
<a-space>
|
||||||
|
<icon-font type="icon-copy" @click.stop="onCopy" />
|
||||||
|
<icon-font type="icon-delete" style="color: #fff" @click.stop="onDelete" />
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
:style="{ '--ant-primary-color': token.colorPrimaryBorder }"
|
||||||
|
:class="`list-group-item list-group-item-masked designer-item ${
|
||||||
|
selected?.uuid === item.uuid ? 'designer-item-active' : ''
|
||||||
|
}`"
|
||||||
|
@click.stop="onClick"
|
||||||
|
>
|
||||||
|
<form-item-render v-if="item.formItem?.group === 'form'" :item="item.formItem" :disabled="true" />
|
||||||
|
<auxiliary-item-render
|
||||||
|
v-else-if="item.formItem?.group === 'auxiliary'"
|
||||||
|
:item="item.formItem"
|
||||||
|
></auxiliary-item-render>
|
||||||
|
<row-component-item
|
||||||
|
v-else-if="item.formItem.group === 'layout' && item.formItem.type === FormItemTypeEnum.ROW"
|
||||||
|
:data-id="item.uuid"
|
||||||
|
:item="XEUtils.assign(item.formItem, { children: item.children?.map((item) => item.formItem) })"
|
||||||
|
>
|
||||||
|
<template v-if="item.children">
|
||||||
|
<col-component-item
|
||||||
|
v-for="(c, index) in item.children"
|
||||||
|
:key="index"
|
||||||
|
:item="c"
|
||||||
|
:components="components"
|
||||||
|
:style="{
|
||||||
|
borderStyle: selected && selected.uuid === c.uuid ? 'solid' : 'dashed',
|
||||||
|
borderWidth: selected && selected.uuid === c.uuid ? '2px' : '1px',
|
||||||
|
borderColor: token.colorPrimaryBorder,
|
||||||
|
}"
|
||||||
|
:selected="selected"
|
||||||
|
@click.stop="emit('selected', c)"
|
||||||
|
@selected-change="(_c?: FormDesignerItem) => emit('selected', _c)"
|
||||||
|
></col-component-item>
|
||||||
|
</template>
|
||||||
|
</row-component-item>
|
||||||
|
</div>
|
||||||
|
</a-badge-ribbon>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { PropType } from 'vue'
|
||||||
|
import { FormDesignerItem } from '../form-designer-types'
|
||||||
|
import { theme } from 'ant-design-vue'
|
||||||
|
import { FormItemTypeEnum } from '@/components/form-render/form-render-types'
|
||||||
|
import XEUtils from 'xe-utils'
|
||||||
|
const { useToken } = theme
|
||||||
|
const { token } = useToken()
|
||||||
|
const props = defineProps({
|
||||||
|
item: {
|
||||||
|
type: Object as PropType<FormDesignerItem>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
type: Object as PropType<FormDesignerItem>,
|
||||||
|
required: false,
|
||||||
|
default: () => null,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
type: Array as PropType<Array<FormDesignerItem>>,
|
||||||
|
required: false,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const { item, selected, components } = toRefs(props)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
selected,
|
||||||
|
(v) => {
|
||||||
|
if (v && item.value.uuid !== v.uuid && v.formItem.type === FormItemTypeEnum.COL) {
|
||||||
|
//嵌套的列
|
||||||
|
if (item.value.children) {
|
||||||
|
const index = item.value.children?.findIndex((i) => i.uuid === v.uuid)
|
||||||
|
item.value.children.splice(index, 1, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
)
|
||||||
|
const emit = defineEmits<{
|
||||||
|
selected: [itemConfig?: FormDesignerItem]
|
||||||
|
copy: [itemConfig: FormDesignerItem]
|
||||||
|
delete: [itemConfig: FormDesignerItem]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
emit('selected', item.value)
|
||||||
|
}
|
||||||
|
const onCopy = () => {
|
||||||
|
emit('copy', item.value)
|
||||||
|
}
|
||||||
|
const onDelete = () => {
|
||||||
|
emit('delete', item.value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.designer-item {
|
||||||
|
padding: 10px 50px 10px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px dashed var(--ant-primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.designer-item-active {
|
||||||
|
border: 2px solid var(--ant-primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item-masked {
|
||||||
|
z-index: 3000;
|
||||||
|
background: rgb(238 236 236 / 30%);
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<a-drawer v-model:open="visible" title="表单预览" width="50%" :closable="false">
|
||||||
|
<form-render v-model="formData" :items="items" :config="config"></form-render>
|
||||||
|
<template #extra>
|
||||||
|
<a-button type="primary" size="small" @click="handleOk">
|
||||||
|
<template #icon>
|
||||||
|
<icon-font type="icon-submit"></icon-font>
|
||||||
|
</template>
|
||||||
|
确定
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { FormConfig } from '@/components/form-render/form-render-types'
|
||||||
|
import { FormDesignerItem } from '../form-designer-types'
|
||||||
|
import { PropType, computed, ref, toRefs } from 'vue'
|
||||||
|
import { notification } from 'ant-design-vue'
|
||||||
|
import XEUtils from 'xe-utils'
|
||||||
|
const visible = ref<boolean>(false)
|
||||||
|
const show = () => {
|
||||||
|
visible.value = true
|
||||||
|
formData.value = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ show })
|
||||||
|
|
||||||
|
const handleOk = () => {
|
||||||
|
notification.open({
|
||||||
|
message: '表单数据',
|
||||||
|
description: JSON.stringify(formData.value),
|
||||||
|
duration: 0,
|
||||||
|
})
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
components: {
|
||||||
|
type: Array as PropType<FormDesignerItem[]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
type: Object as PropType<FormConfig>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const { components, config } = toRefs(props)
|
||||||
|
|
||||||
|
const items = computed(() => {
|
||||||
|
return XEUtils.mapTree(components.value, (item) => item.formItem)
|
||||||
|
})
|
||||||
|
|
||||||
|
const formData = ref({})
|
||||||
|
</script>
|
@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<a-row
|
||||||
|
class="drag-row"
|
||||||
|
v-bind="Object.assign({}, $attrs, item.properties)"
|
||||||
|
:style="{ '--ant-primary-color': token.colorPrimaryBorder }"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { FormItem } from '@/components/form-render/form-render-types'
|
||||||
|
import { theme } from 'ant-design-vue'
|
||||||
|
const { useToken } = theme
|
||||||
|
const { token } = useToken()
|
||||||
|
const props = defineProps({
|
||||||
|
item: {
|
||||||
|
type: Object as PropType<FormItem>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const { item } = toRefs(props)
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.ant-row {
|
||||||
|
min-height: 50px;
|
||||||
|
border: 1px dashed var(--ant-primary-color);
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,91 @@
|
|||||||
|
<template>
|
||||||
|
<a-card size="small">
|
||||||
|
<template #title>
|
||||||
|
<a-tabs centered size="small">
|
||||||
|
<a-tab-pane key="form" tab="组件区" />
|
||||||
|
</a-tabs>
|
||||||
|
</template>
|
||||||
|
<div :style="{ minHeight: 'calc(100vh - 190px)', maxHeight: 'calc(100vh - 190px)', overflowY: 'auto' }">
|
||||||
|
<a-card id="form-items" title="表单组件" size="small">
|
||||||
|
<a-card-grid
|
||||||
|
v-for="element in formComponents"
|
||||||
|
:key="element.uuid"
|
||||||
|
:data-id="element.uuid"
|
||||||
|
:data-type="element.type"
|
||||||
|
class="list-group-item"
|
||||||
|
style="width: 25%; padding: 0 5px; text-align: center"
|
||||||
|
>
|
||||||
|
<input-item :name="element.name" :icon="element.icon" />
|
||||||
|
</a-card-grid>
|
||||||
|
</a-card>
|
||||||
|
<a-card id="auxiliary-items" title="辅助组件" size="small">
|
||||||
|
<a-card-grid
|
||||||
|
v-for="element in auxiliaryComponents"
|
||||||
|
:key="element.uuid"
|
||||||
|
:data-id="element.uuid"
|
||||||
|
:data-type="element.type"
|
||||||
|
class="list-group-item"
|
||||||
|
style="width: 25%; padding: 0 5px; text-align: center"
|
||||||
|
>
|
||||||
|
<input-item :name="element.name" :icon="element.icon" />
|
||||||
|
</a-card-grid>
|
||||||
|
</a-card>
|
||||||
|
<a-card id="layout-items" title="布局组件" size="small">
|
||||||
|
<a-card-grid
|
||||||
|
v-for="element in layoutComponents"
|
||||||
|
:key="element.uuid"
|
||||||
|
:data-id="element.uuid"
|
||||||
|
:data-type="element.type"
|
||||||
|
class="list-group-item"
|
||||||
|
style="width: 25%; padding: 0 5px; text-align: center"
|
||||||
|
>
|
||||||
|
<input-item :name="element.name" :icon="element.icon" />
|
||||||
|
</a-card-grid>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import components from './components'
|
||||||
|
import Sortable, { Options } from 'sortablejs'
|
||||||
|
|
||||||
|
const formComponents = computed(() => {
|
||||||
|
return components.filter((item) => item.formItem?.group === 'form')
|
||||||
|
})
|
||||||
|
const auxiliaryComponents = computed(() => {
|
||||||
|
return components.filter((item) => item.formItem?.group === 'auxiliary')
|
||||||
|
})
|
||||||
|
|
||||||
|
const layoutComponents = computed(() => {
|
||||||
|
return components.filter((item) => item.formItem?.group === 'layout')
|
||||||
|
})
|
||||||
|
|
||||||
|
const auxiliarySort = ref<Sortable>()
|
||||||
|
const formSort = ref<Sortable>()
|
||||||
|
const layoutSort = ref<Sortable>()
|
||||||
|
|
||||||
|
const dragOptions = (): Options => {
|
||||||
|
return {
|
||||||
|
animation: 150,
|
||||||
|
sort: false,
|
||||||
|
group: { name: 'components', pull: 'clone', put: false },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
const formElement = document.querySelector('#form-items .ant-card-body')
|
||||||
|
if (formElement) {
|
||||||
|
formSort.value = Sortable.create(formElement as HTMLElement, dragOptions())
|
||||||
|
}
|
||||||
|
|
||||||
|
const auxiliaryElement = document.querySelector('#auxiliary-items .ant-card-body')
|
||||||
|
if (auxiliaryElement) {
|
||||||
|
auxiliarySort.value = Sortable.create(auxiliaryElement as HTMLElement, dragOptions())
|
||||||
|
}
|
||||||
|
|
||||||
|
const layoutElement = document.querySelector('#layout-items .ant-card-body')
|
||||||
|
if (layoutElement) {
|
||||||
|
layoutSort.value = Sortable.create(layoutElement as HTMLElement, dragOptions())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
4703
src/components/form-designer/component-panel/components.ts
Normal file
23
src/components/form-designer/component-panel/input-item.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<a-space direction="vertical" style="margin-top: 10px">
|
||||||
|
<a-avatar shape="square">
|
||||||
|
<template #icon>
|
||||||
|
<icon-font :type="icon"></icon-font>
|
||||||
|
</template>
|
||||||
|
</a-avatar>
|
||||||
|
<a-typography-paragraph>{{ name }}</a-typography-paragraph>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const props = defineProps({
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const { icon, name } = toRefs(props)
|
||||||
|
</script>
|
107
src/components/form-designer/config-panel/config-panel.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<a-card size="small">
|
||||||
|
<template #title>
|
||||||
|
<a-tabs v-model:active-key="activeKey" centered size="small">
|
||||||
|
<a-tab-pane v-if="currentItem.uuid" key="item" tab="组件配置" />
|
||||||
|
<a-tab-pane key="form" tab="表单配置" />
|
||||||
|
</a-tabs>
|
||||||
|
</template>
|
||||||
|
<div :style="{ minHeight: 'calc(100vh - 190px)', maxHeight: 'calc(100vh - 190px)', overflowY: 'auto' }">
|
||||||
|
<div v-if="activeKey === 'form'">
|
||||||
|
<form-config-panel :form-config="formConfig" @update:form-config="onUpdateFormConfig"></form-config-panel>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<a-tabs v-model:active-key="currentSection" size="small" animated>
|
||||||
|
<a-tab-pane v-if="currentItem.formItem?.group === 'form'" key="config" tab="组件配置">
|
||||||
|
<form-item-config-panel
|
||||||
|
:form-item="currentItem.formItem"
|
||||||
|
:form-config="formConfig"
|
||||||
|
@update:form-item-config="onUpdateFormItemConfig"
|
||||||
|
></form-item-config-panel>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="attribute" tab="组件属性">
|
||||||
|
<property-config :form-item="currentItem.formItem" @update:form-item="onUpdateFormItem"></property-config>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane v-if="currentItem.formItem?.group === 'form'" key="rule" tab="验证规则">
|
||||||
|
<validate-rule-config
|
||||||
|
:rules="currentItem.formItem.rules"
|
||||||
|
@update:rules="onUpdateRules"
|
||||||
|
></validate-rule-config>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { FormConfig, FormItem, FormItemConfig } from '@/components/form-render/form-render-types'
|
||||||
|
import { FormDesignerItem } from '../form-designer-types'
|
||||||
|
import XEUtils from 'xe-utils'
|
||||||
|
import { Rule } from 'ant-design-vue/es/form'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
currentItem: {
|
||||||
|
type: Object as PropType<FormDesignerItem>,
|
||||||
|
required: false,
|
||||||
|
default: () => {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
formConfig: {
|
||||||
|
type: Object as PropType<FormConfig>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const { currentItem, formConfig } = toRefs(props)
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:form-item': [formItem: FormDesignerItem]
|
||||||
|
'update:form-config': [formConfig: FormConfig]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const activeKey = ref('form')
|
||||||
|
const currentSection = ref('config')
|
||||||
|
watch(
|
||||||
|
currentItem,
|
||||||
|
(newVal) => {
|
||||||
|
activeKey.value = newVal.uuid ? 'item' : 'form'
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
const itemType = computed(() => {
|
||||||
|
return currentItem.value.formItem.group
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
itemType,
|
||||||
|
(v) => {
|
||||||
|
currentSection.value = v === 'form' ? 'config' : 'attribute'
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
const onUpdateFormConfig = (newFormConfig: FormConfig) => {
|
||||||
|
emit('update:form-config', newFormConfig)
|
||||||
|
}
|
||||||
|
const onUpdateFormItemConfig = (_formItemConfig: FormItemConfig) => {
|
||||||
|
emit(
|
||||||
|
'update:form-item',
|
||||||
|
XEUtils.assign(currentItem.value, {
|
||||||
|
formItem: XEUtils.assign(currentItem.value.formItem, { config: _formItemConfig }),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const onUpdateFormItem = (formItem: FormItem) => {
|
||||||
|
emit('update:form-item', XEUtils.assign(currentItem.value, { formItem }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const onUpdateRules = (_rules: Rule[]) => {
|
||||||
|
emit(
|
||||||
|
'update:form-item',
|
||||||
|
XEUtils.assign(currentItem.value, {
|
||||||
|
formItem: XEUtils.assign(currentItem.value.formItem, { rules: _rules }),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</script>
|
187
src/components/form-designer/config-panel/data-source-table.vue
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
<template>
|
||||||
|
<a-table size="small" :data-source="shownDataSource" :columns="columns" :pagination="false" row-key="uuid">
|
||||||
|
<template #title>
|
||||||
|
<a-button type="primary" size="small" @click="add">
|
||||||
|
<template #icon>
|
||||||
|
<icon-font type="icon-plus"></icon-font>
|
||||||
|
</template>
|
||||||
|
添加
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'ops'">
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" size="small" @click="edit(record as DataSource)">
|
||||||
|
<template #icon>
|
||||||
|
<icon-font type="icon-edit"></icon-font>
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
<a-popconfirm title="确定删除这个选项?" @confirm="deleteDataSource(record as DataSource)">
|
||||||
|
<a-button type="primary" size="small" danger>
|
||||||
|
<template #icon>
|
||||||
|
<icon-font type="icon-delete"></icon-font>
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<form-modal ref="formModal" v-model="data" :form-items="formItems" :title="title" @ok="hanldOk"></form-modal>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import FormModal from '@/components/form-render/form-modal.vue'
|
||||||
|
import { DataSource, FormDataType, FormItem, FormItemTypeEnum } from '@/components/form-render/form-render-types'
|
||||||
|
import { PropType } from 'vue'
|
||||||
|
import XEUtils from 'xe-utils'
|
||||||
|
const props = defineProps({
|
||||||
|
dataSource: {
|
||||||
|
type: Array as PropType<Array<DataSource>>,
|
||||||
|
required: false,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const { dataSource } = toRefs(props)
|
||||||
|
|
||||||
|
const shownDataSource = computed(() => {
|
||||||
|
return XEUtils.mapTree(XEUtils.clone(dataSource.value, true), (item) => {
|
||||||
|
return XEUtils.assign(XEUtils.clone(item, true), { uuid: XEUtils.uniqueId() })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '键',
|
||||||
|
dataIndex: 'key',
|
||||||
|
key: 'key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '标题',
|
||||||
|
dataIndex: 'title',
|
||||||
|
key: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '描述',
|
||||||
|
dataIndex: 'description',
|
||||||
|
key: 'description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'ops',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const data = ref<Partial<DataSource>>({})
|
||||||
|
const isAdd = ref(true)
|
||||||
|
const title = computed(() => {
|
||||||
|
return isAdd.value ? '添加数据源' : '编辑数据源'
|
||||||
|
})
|
||||||
|
const formItems = reactive<FormItem[]>([
|
||||||
|
{
|
||||||
|
group: 'form',
|
||||||
|
type: FormItemTypeEnum.INPUT,
|
||||||
|
config: {
|
||||||
|
autoLink: true,
|
||||||
|
hasFeedback: false,
|
||||||
|
label: '键',
|
||||||
|
name: 'key',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
size: 'small',
|
||||||
|
type: 'text',
|
||||||
|
allowClear: true,
|
||||||
|
bordered: true,
|
||||||
|
showCount: true,
|
||||||
|
placeholder: '请输入Key',
|
||||||
|
},
|
||||||
|
rules: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'form',
|
||||||
|
type: FormItemTypeEnum.INPUT,
|
||||||
|
config: {
|
||||||
|
autoLink: true,
|
||||||
|
hasFeedback: false,
|
||||||
|
label: '标题',
|
||||||
|
name: 'title',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
size: 'small',
|
||||||
|
type: 'text',
|
||||||
|
allowClear: true,
|
||||||
|
bordered: true,
|
||||||
|
showCount: true,
|
||||||
|
placeholder: '请输入标题',
|
||||||
|
},
|
||||||
|
rules: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: FormItemTypeEnum.TEXTAREA,
|
||||||
|
group: 'form',
|
||||||
|
config: {
|
||||||
|
autoLink: true,
|
||||||
|
hasFeedback: false,
|
||||||
|
label: '描述',
|
||||||
|
name: 'description',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
size: 'small',
|
||||||
|
visibilityToggle: true,
|
||||||
|
allowClear: true,
|
||||||
|
bordered: true,
|
||||||
|
showCount: true,
|
||||||
|
autoSize: {
|
||||||
|
minRows: 2,
|
||||||
|
maxRows: 6,
|
||||||
|
},
|
||||||
|
minRows: 2,
|
||||||
|
maxRows: 6,
|
||||||
|
placeholder: '请输入描述',
|
||||||
|
},
|
||||||
|
rules: [],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const formModal = ref<typeof FormModal>()
|
||||||
|
|
||||||
|
const add = () => {
|
||||||
|
data.value = {}
|
||||||
|
isAdd.value = true
|
||||||
|
formModal.value?.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
const edit = (_dataSource: DataSource) => {
|
||||||
|
data.value = _dataSource
|
||||||
|
isAdd.value = false
|
||||||
|
formModal.value?.open()
|
||||||
|
}
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:data-source': [value: DataSource[]]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const deleteDataSource = (_dataSource: DataSource & { uuid?: string }) => {
|
||||||
|
emit(
|
||||||
|
'update:data-source',
|
||||||
|
XEUtils.filter(shownDataSource.value, (item) => item.uuid !== _dataSource.uuid),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const hanldOk = (_data: FormDataType) => {
|
||||||
|
let d = XEUtils.clone(shownDataSource.value, true)
|
||||||
|
if (_data.uuid) {
|
||||||
|
d = XEUtils.map(d, (item: DataSource & { uuid: string }) => {
|
||||||
|
if (item.uuid === _data.uuid) {
|
||||||
|
return _data as DataSource & { uuid: string }
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
d.push(_data as DataSource & { uuid: string })
|
||||||
|
}
|
||||||
|
emit('update:data-source', d)
|
||||||
|
formModal.value?.close()
|
||||||
|
}
|
||||||
|
</script>
|