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