🆕 项目初始化,页面布局待修改完善

This commit is contained in:
my_ong 2024-12-01 19:02:48 +08:00
commit 6ebf253598
229 changed files with 32894 additions and 0 deletions

5
README.md Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

34
pont-config.json Normal file
View 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
View 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
View 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

Binary file not shown.

BIN
public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

1
public/vite.svg Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,11 @@
import permission from './permission';
import role from './role';
import user from './user';
export default {
permission,
role,
user,
};

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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,
};

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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,
};

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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,
};

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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

File diff suppressed because it is too large Load Diff

118
src/api/api.d.ts vendored Normal file
View 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
View 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;
}
}

View 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));
}

View File

@ -0,0 +1,13 @@
/**
* @description
*
*/
import currentUser from './currentUser';
import login from './login';
import logout from './logout';
export default {
currentUser,
login,
logout,
};

View 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));
}

View 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));
}

View File

@ -0,0 +1,5 @@
import auth from './auth';
export default {
auth,
};

62
src/api/dictionary/api.d.ts vendored Normal file
View 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;
}
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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,
};

View 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));
}

View 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));
}

View File

@ -0,0 +1,5 @@
import dictionary from './dictionary';
export default {
dictionary,
};

19
src/api/index.ts Normal file
View 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
View 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;
}
}

View 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));
}

View 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));
}

View File

@ -0,0 +1,13 @@
/**
* @description /
*
*/
import applies from './applies';
import saveOrUpdateApply from './saveOrUpdateApply';
import detail from './detail';
export default {
applies,
saveOrUpdateApply,
detail,
};

View 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));
}

View 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));
}

View File

@ -0,0 +1,8 @@
import apply from './apply';
import material from './material';
export default {
apply,
material,
};

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View 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,
};

View 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));
}

View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/assets/WORD.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
src/assets/crun.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
src/assets/excel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
src/assets/iam.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

BIN
src/assets/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
src/assets/login-bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

1
src/assets/vue.svg Normal file
View 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
View 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
View 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']
}
}

View File

@ -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 [];
});
// refrender 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

File diff suppressed because it is too large Load Diff

View 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>

View 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>

View 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>

Some files were not shown because too many files have changed in this diff Show More