Compare commits

...

3 Commits

Author SHA1 Message Date
zzq 6431118316 表单设计模块更新 2024-03-08 08:48:07 +08:00
zzq f8d0ea1966 表单设计页面 2024-03-05 15:18:55 +08:00
zzq e54b264bd4 添加表单设计页面 2024-03-02 17:03:40 +08:00
34 changed files with 3184 additions and 6 deletions

View File

@ -10,6 +10,7 @@
/>
<title><%= VITE_GLOB_APP_TITLE %></title>
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="learunui/learunui.css">
</head>
<body>
<div id="app">
@ -154,5 +155,7 @@
</div>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,24 @@
import { defHttp } from '@/utils/http/lraxios';
import { DemoOptionsItem, selectParams, AccountListGetResultModel, AccountParams } from './model/index';
enum Api {
OPTIONS_LIST = '/data/dataitem/details/FormSort',
FORMS_LIST = '/custmerform/scheme/page',
DATABASE_LIST = '/data/codetable/page',
OUTKEY_LIST = '/data/codetables/lrsystemdb'
}
/**
* @description: Get sample options value
*/
export const optionsListApi = (params?: selectParams) =>
defHttp.get<DemoOptionsItem[]>({ url: Api.OPTIONS_LIST, params });
export const getFormGroupList = (params: AccountParams) =>
defHttp.get<AccountListGetResultModel>({ url: Api.FORMS_LIST, params });
export const getDataBaseTableList = (params: AccountParams) =>
defHttp.get<AccountListGetResultModel>({ url: Api.DATABASE_LIST, params });
export const getOutKeyList = (params: AccountParams) =>
defHttp.get<AccountListGetResultModel>({ url: Api.OUTKEY_LIST, params });

View File

@ -0,0 +1,33 @@
import { BasicPageParams, BasicFetchResult } from '@/api/model/baseModel';
export interface DemoOptionsItem {
name: string;
id: string;
}
export interface selectParams {
id: number | string;
}
export type AccountParams = BasicPageParams & {
account?: string;
nickname?: string;
[key: string]: any;
};
export interface AccountListItem {
id: string;
account: string;
email: string;
nickname: string;
role: number;
createTime: string;
remark: string;
status: number;
}
/**
* @description: Request list return value
*/
export type DemoOptionsGetResultModel = BasicFetchResult<DemoOptionsItem>;
export type AccountListGetResultModel = BasicFetchResult<AccountListItem>;

View File

@ -0,0 +1,7 @@
import lrLayout from './src/lrLayout.vue'
lrLayout.install = function(Vue) {
Vue.component(lrLayout.name, lrLayout)
}
export default lrLayout

View File

@ -0,0 +1,208 @@
<template>
<div class="l-layout" :style="{'padding-left':leftWidth}">
<div class="l-layout--left" :style="{'width':leftWidth}" >
<div class="l-layout--wrapper" ><slot name="left"></slot></div>
<div v-if="leftMove" class="l-layout--move" @mousedown="onMousedown('left',$event)" ></div>
</div>
<div class="l-layout--container" :style="{'padding-right':rightWidth}" >
<div class="l-layout--right" :style="{ 'width':rightWidth}">
<div class="l-layout--wrapper" ><slot name="right"></slot></div>
<div v-if="rightMove" class="l-layout--move" @mousedown="onMousedown('right',$event)" ></div>
</div>
<div class="l-layout--container" :style="{'padding-bottom':bottomHight}" >
<div class="l-layout--bottom" :style="{'height':bottomHight}" >
<div class="l-layout--wrapper" > <slot name="bottom"></slot></div>
<div v-if="bottomMove" class="l-layout--move" @mousedown="onMousedown('bottom',$event)" ></div>
</div>
<div class="l-layout--container" :style="{'padding-top':topHight}" >
<div class="l-layout--top" :style="{'height':topHight}" >
<div class="l-layout--wrapper" ><slot name="top"></slot></div>
<div v-if="topMove" class="l-layout--move" @mousedown="onMousedown('top',$event)" ></div>
</div>
<div class="l-layout--wrapper" ref="mid">
<slot></slot>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name:'l-layout',
props: {
left: {
type: Number,
default: 200
},
leftMove: {
type: Boolean,
default: true
},
right: {
type: Number,
default: 200
},
rightMove:{
type: Boolean,
default: true
},
top: {
type: Number,
default: 60
},
topMove:{
type: Boolean,
default: true
},
bottom:{
type: Number,
default: 60
},
bottomMove:{
type: Boolean,
default: true
},
},
data () {
return {
mleft:this.left,
mright:this.right,
mtop:this.top,
mbottom:this.bottom,
move:{
type:'',
isMove:false,
pageX:0,
pageY:0,
size:0,
h:0,
w:0,
}
};
},
mounted () {
},
watch:{
left(val){
this.mleft = val;
},
right(val){
this.mright = val;
},
top(val){
this.mtop = val;
},
bottom(val){
this.mbottom = val;
}
},
computed:{
leftWidth:function(){
if(this.$slots.left){
return this.mleft + 'px'
}
else{
return '0'
}
},
rightWidth:function(){
if(this.$slots.right){
return this.mright + 'px'
}
else{
return '0'
}
},
topHight:function(){
if(this.$slots.top){
return this.mtop + 'px'
}
else{
return '0'
}
},
bottomHight:function(){
if(this.$slots.bottom){
return this.mbottom + 'px'
}
else{
return '0'
}
}
},
methods:{
onMousedown:function(type,e){
this.move.type = type;
this.move.isMove = true;
this.move.pageX = e.pageX;
this.move.pageY = e.pageY;
this.move.size = this["m"+type];
this.move.h = this.$refs.mid.clientHeight;
this.move.w = this.$refs.mid.clientWidth;
document.onmouseup = this.onMouseup;
document.onmousemove = this.onMousemove;
},
onMousemove:function(e){
if(this.move.isMove){
switch(this.move.type){
case 'left':
var x1 = e.pageX - this.move.pageX;
var left = this.move.size + x1;
if(left < 0){
left = 4;
}
else if(left > this.move.size + this.move.w){
left = this.move.size + this.move.w
}
this.mleft = left;
break;
case 'right':
var x2 = e.pageX - this.move.pageX;
var right = this.move.size - x2;
if(right < 0){
right = 4;
}
else if(right > this.move.size + this.move.w){
right = this.move.size + this.move.w
}
this.mright = right;
break;
case 'top':
var y = e.pageY - this.move.pageY;
var top = this.move.size + y;
if(top < 0){
top = 4;
}
else if(top > this.move.size + this.move.h){
top = this.move.size + this.move.h
}
this.mtop = top;
break;
case 'bottom':
var y2 = e.pageY - this.move.pageY;
var bottom = this.move.size - y2;
if(bottom < 0){
bottom = 4;
}
else if(bottom > this.move.size + this.move.h){
bottom = this.move.size + this.move.h
}
this.mbottom = bottom;
break;
}
}
},
onMouseup:function(){
this.move.isMove = false;
document.onmousemove = document.onmouseup = null;
}
}
}
</script>
<style lang="less">
// @import './index.less';
</style>

View File

@ -0,0 +1,7 @@
import lrPanel from './src/lrPanel.vue'
lrPanel.install = function(Vue) {
Vue.component(lrPanel.name, lrPanel)
}
export default lrPanel

View File

@ -0,0 +1,65 @@
<template>
<div class="l-panel">
<div class="l-panel--warpper" :style="{'padding-top':paddingTop}" >
<div v-if="title || $slots.title" class="l-panel--title" >
<slot name="title">{{ title }}</slot>
</div>
<div v-if="$slots.toolLeft || $slots.toolRight" class="l-panel--tool" :style="{'top':toolTop}" >
<div class="l-panel--tool-left">
<slot name="toolLeft" ></slot>
</div>
<div class="l-panel--tool-right">
<slot name="toolRight" ></slot>
</div>
</div>
<div class="l-panel--body" >
<slot></slot>
</div>
</div>
</div>
</template>
<script>
export default {
name:'l-panel',
props: {
title:String,
loading:{
type:Boolean,
default:false
}
},
data () {
return {
};
},
mounted () {
},
computed:{
paddingTop:function(){
var ptop = 0;
if(this.title || this.$slots.title){
ptop += 40;
}
if(this.$slots.toolLeft || this.$slots.toolRight){
ptop += 40;
}
return ptop + 'px';
},
toolTop:function(){
if(this.title || this.$slots.title){
return '40px'
}
else{
return '0'
}
}
},
methods:{
}
}
</script>
<style lang="less">
// @import './index.less';
</style>

View File

@ -3,13 +3,14 @@ import type { GlobConfig } from '#/config';
import { getAppEnvConfig } from '@/utils/env';
export const useGlobSetting = (): Readonly<GlobConfig> => {
const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_UPLOAD_URL } =
const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_UPLOAD_URL,VITE_LR_API_URL } =
getAppEnvConfig();
// Take global configuration
const glob: Readonly<GlobConfig> = {
title: VITE_GLOB_APP_TITLE,
apiUrl: VITE_GLOB_API_URL,
lrApi: VITE_LR_API_URL,
shortName: VITE_GLOB_APP_TITLE.replace(/\s/g, '_').replace(/-/g, '_'),
urlPrefix: VITE_GLOB_API_URL_PREFIX,
uploadUrl: VITE_GLOB_UPLOAD_URL,

View File

@ -68,7 +68,6 @@ async function getAsyncMenus() {
}
if (isRouteMappingMode()) {
// 333333
console.log(permissionStore.getFrontMenuList)
return menuFilter(permissionStore.getFrontMenuList);
}
return staticMenus;

View File

@ -32,12 +32,14 @@ export function getAppEnvConfig() {
: (window[ENV_NAME] as unknown as GlobEnvConfig);
const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_UPLOAD_URL } = ENV;
let { VITE_GLOB_API_URL } = ENV;
let { VITE_LR_API_URL } = ENV;
if (localStorage.getItem(API_ADDRESS)) {
const address = JSON.parse(localStorage.getItem(API_ADDRESS) || '{}');
if (address?.key) VITE_GLOB_API_URL = address?.val;
}
return {
VITE_GLOB_APP_TITLE,
VITE_LR_API_URL,
VITE_GLOB_API_URL,
VITE_GLOB_API_URL_PREFIX,
VITE_GLOB_UPLOAD_URL,

View File

@ -23,7 +23,7 @@ import axios from 'axios';
const globSetting = useGlobSetting();
const urlPrefix = globSetting.urlPrefix;
const { createMessage, createErrorModal, createSuccessModal } = useMessage();
console.log('globSetting',globSetting)
/**
* @description: 便
*/
@ -52,7 +52,6 @@ const transform: AxiosTransform = {
}
// 这里 coderesultmessage为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
const { code, result, message } = data;
// 这里逻辑可以根据项目进行修改
const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS;
if (hasSuccess) {

View File

@ -0,0 +1,251 @@
import type {
AxiosRequestConfig,
AxiosInstance,
AxiosResponse,
AxiosError,
InternalAxiosRequestConfig,
} from 'axios';
import type { RequestOptions, Result, UploadFileParams } from '#/axios';
import type { CreateAxiosOptions } from './axiosTransform';
import axios from 'axios';
import qs from 'qs';
import { AxiosCanceler } from './axiosCancel';
import { isFunction } from '@/utils/is';
import { cloneDeep } from 'lodash-es';
import { ContentTypeEnum, RequestEnum } from '@/enums/httpEnum';
export * from './axiosTransform';
/**
* @description: axios module
*/
export class VAxios {
private axiosInstance: AxiosInstance;
private readonly options: CreateAxiosOptions;
constructor(options: CreateAxiosOptions) {
this.options = options;
this.axiosInstance = axios.create(options);
this.setupInterceptors();
}
/**
* @description: Create axios instance
*/
private createAxios(config: CreateAxiosOptions): void {
this.axiosInstance = axios.create(config);
}
private getTransform() {
const { transform } = this.options;
return transform;
}
getAxios(): AxiosInstance {
return this.axiosInstance;
}
/**
* @description: Reconfigure axios
*/
configAxios(config: CreateAxiosOptions) {
if (!this.axiosInstance) {
return;
}
this.createAxios(config);
}
/**
* @description: Set general header
*/
setHeader(headers: any): void {
if (!this.axiosInstance) {
return;
}
Object.assign(this.axiosInstance.defaults.headers, headers);
}
/**
* @description: Interceptor configuration
*/
private setupInterceptors() {
// const transform = this.getTransform();
const {
axiosInstance,
options: { transform },
} = this;
if (!transform) {
return;
}
const {
requestInterceptors,
requestInterceptorsCatch,
responseInterceptors,
responseInterceptorsCatch,
} = transform;
const axiosCanceler = new AxiosCanceler();
// Request interceptor configuration processing
this.axiosInstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
// If cancel repeat request is turned on, then cancel repeat request is prohibited
const requestOptions =
(config as unknown as any).requestOptions ?? this.options.requestOptions;
const ignoreCancelToken = requestOptions?.ignoreCancelToken ?? true;
!ignoreCancelToken && axiosCanceler.addPending(config);
if (requestInterceptors && isFunction(requestInterceptors)) {
config = requestInterceptors(config, this.options);
}
return config;
}, undefined);
// Request interceptor error capture
requestInterceptorsCatch &&
isFunction(requestInterceptorsCatch) &&
this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);
// Response result interceptor processing
this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
res && axiosCanceler.removePending(res.config);
if (responseInterceptors && isFunction(responseInterceptors)) {
res = responseInterceptors(res);
}
return res;
}, undefined);
// Response result interceptor error capture
responseInterceptorsCatch &&
isFunction(responseInterceptorsCatch) &&
this.axiosInstance.interceptors.response.use(undefined, (error) => {
return responseInterceptorsCatch(axiosInstance, error);
});
}
/**
* @description: File Upload
*/
uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
const formData = new window.FormData();
const customFilename = params.name || 'file';
if (params.filename) {
formData.append(customFilename, params.file, params.filename);
} else {
formData.append(customFilename, params.file);
}
if (params.data) {
Object.keys(params.data).forEach((key) => {
const value = params.data![key];
if (Array.isArray(value)) {
value.forEach((item) => {
formData.append(`${key}[]`, item);
});
return;
}
formData.append(key, params.data![key]);
});
}
return this.axiosInstance.request<T>({
...config,
method: 'POST',
data: formData,
headers: {
'Content-type': ContentTypeEnum.FORM_DATA,
// @ts-ignore
ignoreCancelToken: true,
},
});
}
// support form-data
supportFormData(config: AxiosRequestConfig) {
const headers = config.headers || this.options.headers;
const contentType = headers?.['Content-Type'] || headers?.['content-type'];
if (
contentType !== ContentTypeEnum.FORM_URLENCODED ||
!Reflect.has(config, 'data') ||
config.method?.toUpperCase() === RequestEnum.GET
) {
return config;
}
return {
...config,
data: qs.stringify(config.data, { arrayFormat: 'brackets' }),
};
}
get<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
return this.request({ ...config, method: 'GET' }, options);
}
post<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
return this.request({ ...config, method: 'POST' }, options);
}
put<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
return this.request({ ...config, method: 'PUT' }, options);
}
delete<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
return this.request({ ...config, method: 'DELETE' }, options);
}
request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
let conf: CreateAxiosOptions = cloneDeep(config);
// cancelToken 如果被深拷贝会导致最外层无法使用cancel方法来取消请求
if (config.cancelToken) {
conf.cancelToken = config.cancelToken;
}
if (config.signal) {
conf.signal = config.signal;
}
const transform = this.getTransform();
const { requestOptions } = this.options;
const opt: RequestOptions = Object.assign({}, requestOptions, options);
const { beforeRequestHook, requestCatchHook, transformResponseHook } = transform || {};
if (beforeRequestHook && isFunction(beforeRequestHook)) {
conf = beforeRequestHook(conf, opt);
}
conf.requestOptions = opt;
conf = this.supportFormData(conf);
return new Promise((resolve, reject) => {
this.axiosInstance
.request<any, AxiosResponse<Result>>(conf)
.then((res: AxiosResponse<Result>) => {
if (transformResponseHook && isFunction(transformResponseHook)) {
try {
const ret = transformResponseHook(res, opt);
resolve(ret);
} catch (err) {
reject(err || new Error('request error!'));
}
return;
}
resolve(res as unknown as Promise<T>);
})
.catch((e: Error | AxiosError) => {
if (requestCatchHook && isFunction(requestCatchHook)) {
reject(requestCatchHook(e, opt));
return;
}
if (axios.isAxiosError(e)) {
// rewrite error message from axios in here
}
reject(e);
});
});
}
}

View File

@ -0,0 +1,60 @@
import type { AxiosRequestConfig } from 'axios';
// 用于存储每个请求的标识和取消函数
const pendingMap = new Map<string, AbortController>();
const getPendingUrl = (config: AxiosRequestConfig): string => {
return [config.method, config.url].join('&');
};
export class AxiosCanceler {
/**
*
* @param config
*/
public addPending(config: AxiosRequestConfig): void {
this.removePending(config);
const url = getPendingUrl(config);
const controller = new AbortController();
config.signal = config.signal || controller.signal;
if (!pendingMap.has(url)) {
// 如果当前请求不在等待中,将其添加到等待中
pendingMap.set(url, controller);
}
}
/**
*
*/
public removeAllPending(): void {
pendingMap.forEach((abortController) => {
if (abortController) {
abortController.abort();
}
});
this.reset();
}
/**
*
* @param config
*/
public removePending(config: AxiosRequestConfig): void {
const url = getPendingUrl(config);
if (pendingMap.has(url)) {
// 如果当前请求在等待中,取消它并将其从等待中移除
const abortController = pendingMap.get(url);
if (abortController) {
abortController.abort(url);
}
pendingMap.delete(url);
}
}
/**
*
*/
public reset(): void {
pendingMap.clear();
}
}

View File

@ -0,0 +1,30 @@
import { AxiosError, AxiosInstance } from 'axios';
/**
*
*/
export class AxiosRetry {
/**
*
*/
retry(axiosInstance: AxiosInstance, error: AxiosError) {
// @ts-ignore
const { config } = error.response;
const { waitTime, count } = config?.requestOptions?.retryRequest ?? {};
config.__retryCount = config.__retryCount || 0;
if (config.__retryCount >= count) {
return Promise.reject(error);
}
config.__retryCount += 1;
//请求返回后config的header不正确造成重试请求失败,删除返回headers采用默认headers
delete config.headers;
return this.delay(waitTime).then(() => axiosInstance(config));
}
/**
*
*/
private delay(waitTime: number) {
return new Promise((resolve) => setTimeout(resolve, waitTime));
}
}

View File

@ -0,0 +1,57 @@
/**
* Data processing class, can be configured according to the project
*/
import type {
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
InternalAxiosRequestConfig,
} from 'axios';
import type { RequestOptions, Result } from '#/axios';
export interface CreateAxiosOptions extends AxiosRequestConfig {
authenticationScheme?: string;
transform?: AxiosTransform;
requestOptions?: RequestOptions;
}
export abstract class AxiosTransform {
/**
* A function that is called before a request is sent. It can modify the request configuration as needed.
*
*/
beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig;
/**
* @description:
*/
transformResponseHook?: (res: AxiosResponse<Result>, options: RequestOptions) => any;
/**
* @description:
*/
requestCatchHook?: (e: Error, options: RequestOptions) => Promise<any>;
/**
* @description:
*/
requestInterceptors?: (
config: InternalAxiosRequestConfig,
options: CreateAxiosOptions,
) => InternalAxiosRequestConfig;
/**
* @description:
*/
responseInterceptors?: (res: AxiosResponse<any>) => AxiosResponse<any>;
/**
* @description:
*/
requestInterceptorsCatch?: (error: Error) => void;
/**
* @description:
*/
responseInterceptorsCatch?: (axiosInstance: AxiosInstance, error: Error) => void;
}

View File

@ -0,0 +1,80 @@
import type { ErrorMessageMode } from '#/axios';
import { useMessage } from '@/hooks/web/useMessage';
import { useI18n } from '@/hooks/web/useI18n';
// import router from '@/router';
// import { PageEnum } from '@/enums/pageEnum';
import { useUserStoreWithOut } from '@/store/modules/user';
import projectSetting from '@/settings/projectSetting';
import { SessionTimeoutProcessingEnum } from '@/enums/appEnum';
const { createMessage, createErrorModal } = useMessage();
const error = createMessage.error!;
const stp = projectSetting.sessionTimeoutProcessing;
export function checkStatus(
status: number,
msg: string,
errorMessageMode: ErrorMessageMode = 'message',
): void {
const { t } = useI18n();
const userStore = useUserStoreWithOut();
let errMessage = '';
switch (status) {
case 400:
errMessage = `${msg}`;
break;
// 401: Not logged in
// Jump to the login page if not logged in, and carry the path of the current page
// Return to the current page after successful login. This step needs to be operated on the login page.
case 401:
userStore.setToken(undefined);
errMessage = msg || t('sys.api.errMsg401');
if (stp === SessionTimeoutProcessingEnum.PAGE_COVERAGE) {
userStore.setSessionTimeout(true);
} else {
userStore.logout(true);
}
break;
case 403:
errMessage = t('sys.api.errMsg403');
break;
// 404请求不存在
case 404:
errMessage = t('sys.api.errMsg404');
break;
case 405:
errMessage = t('sys.api.errMsg405');
break;
case 408:
errMessage = t('sys.api.errMsg408');
break;
case 500:
errMessage = t('sys.api.errMsg500');
break;
case 501:
errMessage = t('sys.api.errMsg501');
break;
case 502:
errMessage = t('sys.api.errMsg502');
break;
case 503:
errMessage = t('sys.api.errMsg503');
break;
case 504:
errMessage = t('sys.api.errMsg504');
break;
case 505:
errMessage = t('sys.api.errMsg505');
break;
default:
}
if (errMessage) {
if (errorMessageMode === 'modal') {
createErrorModal({ title: t('sys.api.errorTip'), content: errMessage });
} else if (errorMessageMode === 'message') {
error({ content: errMessage, key: `global_error_message_status_${status}` });
}
}
}

View File

@ -0,0 +1,56 @@
/*
* @Author:
* @Date: 2024-01-13 13:04:15
* @LastEditors: Do not edit
* @LastEditTime: 2024-01-22 08:55:09
* @FilePath: \e:\\vue-vben-admin\src\utils\http\axios\helper.ts
* @Description:
*/
import { isObject, isString } from '@/utils/is';
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
export function joinTimestamp<T extends boolean>(
join: boolean,
restful: T,
): T extends true ? string : object;
export function joinTimestamp(join: boolean, restful = false): string | object {
if (!join) {
return restful ? '' : {};
}
// const now = new Date().getTime();
// if (restful) {
// return `?_t=${now}`;
// }
// return { _t: now };
}
/**
* @description: Format request parameter time
*/
export function formatRequestDate(params: Recordable) {
if (Object.prototype.toString.call(params) !== '[object Object]') {
return;
}
for (const key in params) {
const format = params[key]?.format ?? null;
if (format && typeof format === 'function') {
params[key] = params[key].format(DATE_TIME_FORMAT);
}
if (isString(key)) {
const value = params[key];
if (value) {
try {
params[key] = isString(value) ? value.trim() : value;
} catch (error: any) {
throw new Error(error);
}
}
}
if (isObject(params[key])) {
formatRequestDate(params[key]);
}
}
}

View File

@ -0,0 +1,293 @@
// axios配置 可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动
// The axios configuration can be changed according to the project, just change the file, other files can be left unchanged
import type { AxiosInstance, AxiosResponse } from 'axios';
import { clone } from 'lodash-es';
import type { RequestOptions, Result } from '#/axios';
import type { AxiosTransform, CreateAxiosOptions } from './axiosTransform';
import { VAxios } from './Axios';
import { checkStatus } from './checkStatus';
import { useGlobSetting } from '@/hooks/setting';
import { useMessage } from '@/hooks/web/useMessage';
import { RequestEnum, ResultEnum, ContentTypeEnum } from '@/enums/httpEnum';
import { isString, isUndefined, isNull, isEmpty } from '@/utils/is';
import { getToken } from '@/utils/auth';
import { setObjToUrlParams, deepMerge } from '@/utils';
import { useErrorLogStoreWithOut } from '@/store/modules/errorLog';
import { useI18n } from '@/hooks/web/useI18n';
import { joinTimestamp, formatRequestDate } from './helper';
import { useUserStoreWithOut } from '@/store/modules/user';
import { AxiosRetry } from '@/utils/http/axios/axiosRetry';
import axios from 'axios';
const globSetting = useGlobSetting();
const urlPrefix = globSetting.urlPrefix;
const { createMessage, createErrorModal, createSuccessModal } = useMessage();
/**
* @description: 便
*/
const transform: AxiosTransform = {
/**
* @description:
*/
transformResponseHook: (res: AxiosResponse<Result>, options: RequestOptions) => {
const { t } = useI18n();
const { isTransformResponse, isReturnNativeResponse } = options;
// 是否返回原生响应头 比如:需要获取响应头时使用该属性
if (isReturnNativeResponse) {
return res;
}
// 不进行任何处理,直接返回
// 用于页面代码可能需要直接获取codedatamessage这些信息时开启
if (!isTransformResponse) {
return res.data;
}
// 错误的时候返回
const { data } = res;
if (!data) {
// return '[HTTP] Request has no return value';
throw new Error(t('sys.api.apiRequestFailed'));
}
// 这里 coderesultmessage为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
const { code, result, message } = data;
// 这里逻辑可以根据项目进行修改
const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS;
if (hasSuccess) {
let successMsg = message;
if (isNull(successMsg) || isUndefined(successMsg) || isEmpty(successMsg)) {
successMsg = t(`sys.api.operationSuccess`);
}
if (options.successMessageMode === 'modal') {
createSuccessModal({ title: t('sys.api.successTip'), content: successMsg });
} else if (options.successMessageMode === 'message') {
createMessage.success(successMsg);
}
if(data.data.rows){
return data.data.rows
}else{
return data.data;
}
}
// 在此处根据自己项目的实际情况对不同的code执行不同的操作
// 如果不希望中断当前请求请return数据否则直接抛出异常即可
let timeoutMsg = '';
switch (code) {
case ResultEnum.TIMEOUT:
timeoutMsg = t('sys.api.timeoutMessage');
const userStore = useUserStoreWithOut();
userStore.logout(true);
break;
default:
if (message) {
timeoutMsg = message;
}
}
// errorMessageMode='modal'的时候会显示modal错误弹窗而不是消息提示用于一些比较重要的错误
// errorMessageMode='none' 一般是调用时明确表示不希望自动弹出错误提示
if (options.errorMessageMode === 'modal') {
createErrorModal({ title: t('sys.api.errorTip'), content: timeoutMsg });
} else if (options.errorMessageMode === 'message') {
createMessage.error(timeoutMsg);
}
throw new Error(timeoutMsg || t('sys.api.apiRequestFailed'));
},
// 请求之前处理config
beforeRequestHook: (config, options) => {
const { lrApi, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options;
if (joinPrefix) {
config.url = `${urlPrefix}${config.url}`;
}
if (lrApi && isString(lrApi)) {
config.url = `${lrApi}${config.url}`;
}
const params = config.params || {};
const data = config.data || false;
formatDate && data && !isString(data) && formatRequestDate(data);
if (config.method?.toUpperCase() === RequestEnum.GET) {
if (!isString(params)) {
// 给 get 请求加上时间戳参数,避免从缓存中拿数据。
config.params = Object.assign(params || {}, joinTimestamp(joinTime, false));
} else {
// 兼容restful风格
config.url = config.url + params + `${joinTimestamp(joinTime, true)}`;
config.params = undefined;
}
} else {
if (!isString(params)) {
formatDate && formatRequestDate(params);
if (
Reflect.has(config, 'data') &&
config.data &&
(Object.keys(config.data).length > 0 || config.data instanceof FormData)
) {
config.data = data;
config.params = params;
} else {
// 非GET请求如果没有提供data则将params视为data
config.data = params;
config.params = undefined;
}
if (joinParamsToUrl) {
config.url = setObjToUrlParams(
config.url as string,
Object.assign({}, config.params, config.data),
);
}
} else {
// 兼容restful风格
config.url = config.url + params;
config.params = undefined;
}
}
return config;
},
/**
* @description:
*/
requestInterceptors: (config, options) => {
// 请求之前处理config
const token = getToken();
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
// jwt token
(config as Recordable).headers.Authorization = options.authenticationScheme
? `${options.authenticationScheme} ${token}`
: token;
config.headers['X-Token'] = token;
}
return config;
},
/**
* @description:
*/
responseInterceptors: (res: AxiosResponse<any>) => {
return res;
},
/**
* @description:
*/
responseInterceptorsCatch: (axiosInstance: AxiosInstance, error: any) => {
const { t } = useI18n();
const errorLogStore = useErrorLogStoreWithOut();
errorLogStore.addAjaxErrorInfo(error);
const { response, code, message, config } = error || {};
const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none';
const msg: string = response?.data?.error?.message ?? '';
const err: string = error?.toString?.() ?? '';
let errMessage = '';
if (axios.isCancel(error)) {
return Promise.reject(error);
}
try {
if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
errMessage = t('sys.api.apiTimeoutMessage');
}
if (err?.includes('Network Error')) {
errMessage = t('sys.api.networkExceptionMsg');
}
if (errMessage) {
if (errorMessageMode === 'modal') {
createErrorModal({ title: t('sys.api.errorTip'), content: errMessage });
} else if (errorMessageMode === 'message') {
createMessage.error(errMessage);
}
return Promise.reject(error);
}
} catch (error) {
throw new Error(error as unknown as string);
}
checkStatus(error?.response?.status, msg, errorMessageMode);
// 添加自动重试机制 保险起见 只针对GET请求
const retryRequest = new AxiosRetry();
const { isOpenRetry } = config.requestOptions.retryRequest;
config.method?.toUpperCase() === RequestEnum.GET &&
isOpenRetry &&
// @ts-ignore
retryRequest.retry(axiosInstance, error);
return Promise.reject(error);
},
};
function createAxios(opt?: Partial<CreateAxiosOptions>) {
return new VAxios(
// 深度合并
deepMerge(
{
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
// authentication schemese.g: Bearer
// authenticationScheme: 'Bearer',
authenticationScheme: '',
timeout: 10 * 1000,
// 基础接口地址
// baseURL: globSetting.lrApi,
headers: {
'Content-Type': ContentTypeEnum.JSON,
// 'T-Token':getToken()
'Token':"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDk4NTc4MjMsImV4cCI6MTcwOTkwMTAyMywiVXNlcklkIjoiU3lzdGVtIiwiVXNlck5hbWUiOiLotoXnuqfnrqHnkIblkZgiLCJBY2NvdW50IjoiU3lzdGVtIn0.U2mHpvl4cx81XRP4JRmwrUg0dSXNkFv_EXSJQI1mtLI"
},
// 如果是form-data格式
// headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
// 数据处理方式
transform: clone(transform),
// 配置项,下面的选项都可以在独立的接口请求中覆盖
requestOptions: {
// 默认将prefix 添加到url
joinPrefix: true,
// 是否返回原生响应头 比如:需要获取响应头时使用该属性
isReturnNativeResponse: false,
// 需要对返回数据进行处理
isTransformResponse: true,
// post请求的时候添加参数到url
joinParamsToUrl: false,
// 格式化提交参数时间
formatDate: true,
// 消息提示类型
errorMessageMode: 'message',
// 接口地址
lrApi: globSetting.lrApi,
// 接口拼接地址
urlPrefix: urlPrefix,
// 是否加入时间戳
joinTime: true,
// 忽略重复请求
ignoreCancelToken: true,
// 是否携带token
withToken: true,
retryRequest: {
isOpenRetry: true,
count: 5,
waitTime: 100,
},
},
},
opt || {},
),
);
}
export const defHttp = createAxios();
// other api url
// export const otherHttp = createAxios({
// requestOptions: {
// lrApi: 'xxx',
// urlPrefix: 'xxx',
// },
// });

View File

@ -0,0 +1,42 @@
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:canFullscreen="false"
:defaultFullscreen="true"
:showCancelBtn="false"
:showOkBtn="false"
:draggable="false"
title="慧创 表单设计"
>
<div class="form-box">
<FormPage />
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import FormPage from './form/index.vue';
defineOptions({ name: 'FormModal' });
const emit = defineEmits(['success', 'register']);
const [registerModal, { closeModal, setModalProps }] = useModalInner();
</script>
<style lang="less">
.form-box{
width: 100%;
height: calc(100% - 55px);
position:fixed;
top:55px;
left: 0;
}
</style>

View File

@ -0,0 +1,95 @@
<template>
<div class="m-4 mr-0 overflow-hidden bg-white">
<BasicTree ref="asyncExpandTreeRef" title="菜单列表" toolbar search
treeWrapperClassName="h-[calc(100%-35px)] overflow-auto" loadData :actionList="actionList"
:renderIcon="createIcon" :clickRowToExpand="false" :treeData="treeData" :fieldNames="{ key: 'id', title: 'name' }"
:defaultExpandAll="true" @select="handleSelect" />
<BasicModal @register="register" title="删除" :helpMessage="['提示1', '提示2']" @ok="handleSubmit">
确认要删除菜单吗
</BasicModal>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, h, nextTick, unref } from 'vue';
import { BasicTree, TreeItem, TreeActionItem } from '@/components/Tree';
import { getMenuList, deleteMenu } from '@/api/demo/system';
import { FormOutlined, DeleteOutlined } from '@ant-design/icons-vue';
import { BasicModal, useModal } from '@/components/Modal';
import { router } from '@/router';
const [register, { closeModal, openModal }] = useModal();
defineOptions({ name: 'DeptTree' });
const emit = defineEmits(['select', 'edit']);
const treeData = ref < TreeItem[] > ([]);
let selectItemId = ref('')
const asyncExpandTreeRef = ref < Nullable < TreeActionType >> (null);
async function handleSubmit() {
var query = [selectItemId.value]
const data = await deleteMenu(query);
closeModal();
}
async function fetch() {
treeData.value = (await getMenuList()) as unknown as TreeItem[];
//
nextTick(() => {
unref(asyncExpandTreeRef)?.expandAll(true);
});
}
function handleSelect(keys) {
emit('select', keys[0]);
}
const btnList = router.currentRoute.value.meta.elements
const actionList: TreeActionItem[] = []
btnList.forEach(element => {
if (element.domId == 'btnEdit') {
actionList.push({
render: (node) => {
return h(FormOutlined, {
class: 'ml-2',
onClick: () => {
emit('edit', node);
},
});
},
})
} else if (element.domId == 'btnDelete') {
actionList.push({
render: (node) => {
return h(DeleteOutlined, {
class: 'ml-2',
onClick: () => {
selectItemId.value = node.id
openModal(true, {
isUpdate: false,
});
},
});
},
})
}
});
function createIcon({ level }) {
if (level === 1) {
return 'ion:git-compare-outline';
}
if (level === 2) {
return 'ion:home';
}
if (level === 3) {
return 'ion:airplane';
}
return '';
}
onMounted(() => {
fetch();
});
defineExpose({
fetch
})
</script>

View File

@ -0,0 +1,49 @@
import { BasicColumn, FormSchema } from '@/components/Table';
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
export const columns: BasicColumn[] = [
{
title: '名称',
dataIndex: 'f_Name',
},
{
title: '分类',
dataIndex: 'f_Category_Name',
},
{
title: '类型',
dataIndex: 'status',
width: 80,
customRender: ({ record }) => {
const status = record.f_FormType;
const enable = ~~status === 0;
const color = enable ? '#67c23a' : '#e6a23c';
const text = enable ? '常规表单' : '视图表单';
return h(Tag, { color: color }, () => text);
},
},
{
title: '创建人',
dataIndex: 'f_CreateUserName'
},
{
title: '创建时间',
dataIndex: 'f_CreateDate'
},
// {
// title: '备注',
// dataIndex: 'remark',
// },
];
export const searchFormSchema: FormSchema[] = [
{
field: 'key',
label: '关键字',
component: 'Input',
colProps: { span: 8 },
},
];

View File

@ -0,0 +1,802 @@
<template>
<div class="l-rblock" >
<div v-show="steps(0)" class="l-rblock" style="padding:24px;" >
<div class="l-page-panel" >
<el-form :model="formData" :rules="rules" size="mini" ref="baseInfo" label-width="88px" >
<el-col :span="24">
<el-form-item :label="$t('名称')" prop="f_Name">
<el-input v-model="formData.f_Name"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="$t('分类')" prop="f_Category">
<l-tree-select
v-model="formData.f_Category"
:placeholder="$t('请选择')"
:options="lr_dataItemTree(lr_dataItem['FormSort'])"
>
</l-tree-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="$t('数据库')" prop="f_DbCode">
<el-select v-model="formData.f_DbCode" :placeholder="$t('请选择')">
<el-option-group
v-for="group in lr_dblinkTree"
:key="group.id"
:label="group.label">
<el-option
v-for="item in group.children"
:key="item.id"
:label="item.label"
:value="item.id">
</el-option>
</el-option-group>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="$t('状态')" prop="f_EnabledMark">
<el-switch
:active-value="1"
:inactive-value="0"
v-model="formData.f_EnabledMark"
>
</el-switch>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="$t('类型')" prop="f_FormType" >
<l-radio
:options="[{value:0,label:$t('常规表单')},{value:1,label:$t('视图表单')}]"
v-model="formData.f_FormType"
@change="handleFormTypeChange"
>
</l-radio>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="$t('描述')" prop="f_Description">
<el-input type="textarea" v-model="formData.f_Description"></el-input>
</el-form-item>
</el-col>
</el-form>
<template v-if="formData.f_FormType != 1" >
<el-col :span="24">
<div class="l-title" >{{$t('添加数据库表(请先选择数据库)')}}</div>
</el-col>
<el-col :key="1" :span="24">
<l-edit-table
addBtnText="添加"
:dataSource="dbTableData"
@addRow="addRow"
@deleteRow="deleteRow"
>
<el-table-column
prop="type"
:label="$t('类别')"
width="64"
align="center"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.type == 'main'" size="mini" >主表</el-tag>
<el-tag @click="chlidTagClick(scope.row)" v-else size="mini" style="cursor: pointer;" type="warning">子表</el-tag>
</template>
</el-table-column>
<el-table-column
prop="name"
:label="$t('表名')"
minWidth="100">
</el-table-column>
<el-table-column
prop="field"
:label="$t('外键')"
minWidth="100">
<template v-if="scope.row.type != 'main' " slot-scope="scope">
<el-select size="mini" v-model="scope.row.field" :placeholder="$t('请选择')">
<el-option
v-for="item in scope.row.columns || []"
:key="item.name"
:label="item.name"
:value="item.name">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column
prop="relationName"
:label="$t('关联表')"
minWidth="100">
<template v-if="scope.row.type != 'main' " slot-scope="scope">
<el-select size="mini" v-model="scope.row.relationName" :placeholder="$t('请选择')">
<el-option
v-for="item in relationTables(scope.row.name)"
:key="item.name"
:label="item.name"
:value="item.name">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column
prop="relationField"
:label="$t('关联主键')"
minWidth="100">
<template v-if="scope.row.type != 'main' && scope.row.relationName " slot-scope="scope">
<el-select size="mini" v-model="scope.row.relationField" :placeholder="$t('请选择')">
<el-option
v-for="item in relationTableFields(scope.row.relationName)"
:key="item.name"
:label="item.name"
:value="item.name">
</el-option>
</el-select>
</template>
</el-table-column>
</l-edit-table >
</el-col>
<el-col :span="24">
<div class="l-title" >{{$t('添加数据库关联表,只用来做数据权限设置(请先选择数据库)')}}</div>
</el-col>
<el-col :key="2" :span="24">
<l-edit-table
addBtnText="添加"
:dataSource="dbTableRData"
@addRow="addDbTableRData"
@deleteRow="deleteDbTableRData"
>
<el-table-column
prop="fname"
:label="$t('名称')"
minWidth="160" >
<template slot-scope="scope" >
<el-input size="mini" v-model="scope.row.fname" ></el-input>
</template>
</el-table-column>
<el-table-column
prop="name"
:label="$t('表名')"
minWidth="160">
</el-table-column>
<el-table-column
prop="cfield"
:label="$t('比较字段')"
minWidth="160">
<template slot-scope="scope">
<el-select size="mini" v-model="scope.row.cfield" :placeholder="$t('请选择')">
<el-option
v-for="item in scope.row.columns || []"
:key="item.name"
:label="item.name"
:value="item.name">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column
prop="field"
:label="$t('外键')"
minWidth="160">
<template slot-scope="scope">
<el-select size="mini" v-model="scope.row.field" :placeholder="$t('请选择')">
<el-option
v-for="item in scope.row.columns || []"
:key="item.name"
:label="item.name"
:value="item.name">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column
prop="relationName"
:label="$t('关联表')"
minWidth="160">
<template v-if="scope.row.type != 'main' " slot-scope="scope">
<el-select size="mini" v-model="scope.row.relationName" :placeholder="$t('请选择')">
<el-option
v-for="item in dbTableData"
:key="item.name"
:label="item.name"
:value="item.name">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column
prop="relationField"
:label="$t('关联主键')"
minWidth="160">
<template v-if="scope.row.relationName " slot-scope="scope">
<el-select size="mini" v-model="scope.row.relationField" :placeholder="$t('请选择')">
<el-option
v-for="item in relationTableFields(scope.row.relationName)"
:key="item.name"
:label="item.name"
:value="item.name">
</el-option>
</el-select>
</template>
</el-table-column>
</l-edit-table >
</el-col>
</template>
<template v-else >
<el-col :span="24">
<div class="l-title" >{{$t('添加数据视图(请先选择数据库)')}}</div>
</el-col>
<el-col :span="24">
<l-edit-table
addBtnText="添加"
:dataSource="dbTableData"
@addRow="addSqlData"
@deleteRow="deleteSqlData"
>
<el-table-column
prop="type"
:label="$t('类别')"
width="64"
align="center"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.type == 'main'" size="mini" ></el-tag>
<el-tag v-else size="mini" type="warning"></el-tag>
</template>
</el-table-column>
<el-table-column
prop="name"
:label="$t('名称')"
minWidth="160" >
<el-button type="text" slot-scope="scope" @click.stop="editSqlData(scope.row)" >{{scope.row.name}}</el-button>
</el-table-column>
<el-table-column
prop="relationField"
:label="$t('关联字段')"
minWidth="160" >
<el-select v-if="scope.row.type != 'main'" slot-scope="scope" size="mini" v-model="scope.row.relationField" placeholder="请选择">
<el-option
v-for="item in sqlMainCols || []"
:key="item.name"
:label="item.name"
:value="item.name">
</el-option>
</el-select>
</el-table-column>
</l-edit-table>
</el-col>
</template>
</div>
</div>
<div v-show="steps(1)" class="l-rblock" >
<l-form-design
:dbTables="dbTableData"
ref="formDesign"
></l-form-design>
</div>
<l-codetable-selectdialog
:visible.sync="dbtableSelectdialog"
:dbCode="formData.f_DbCode"
:isOkClose="false"
@select="dbSelect"
>
</l-codetable-selectdialog>
<l-dialog
:title="sqlDialogTitle"
:visible.sync="addSqlDialog"
:width="640"
@ok="sqlDialogSave"
@closed="sqlDialogClosed"
@opened="sqlDialogOpened"
>
<sql-form ref="sqlForm" ></sql-form>
</l-dialog>
</div>
</template>
<script>
const apiScheme = window.$api.custmerForm.scheme
const apiCodeTable = window.$api.data.codeTable
import SqlForm from './sqlForm.vue'
export default {
props: {
stepActive:{
type:Number,
default:0
}
},
components:{
SqlForm
},
data(){
return {
formData:{
f_Name:'',
f_Category:'',
f_DbCode:'',
f_EnabledMark:1,
f_FormType:0,
f_Description:''
},
rules: {
f_Name: [
{ required: true, message: this.$t('请输入'), trigger: 'blur' },
],
f_Category: [
{ required: true, message: this.$t('请选择'), trigger: 'change' }
],
f_DbCode: [
{ required: true, message: this.$t('请选择'), trigger: 'change' }
]
},
dbTableData:[],
dbtableSelectdialog:false,
dbTableRData:[], //
isAddDbTableRData:false,
addSqlDialog:false,
editSql:false,
sqlDialogTitle:'',
sqlDialogRow:null,
isNotFetchTables:false,
tableColumns:{} //
};
},
created () {
this.initData()
},
computed:{
sqlMainCols(){
const table = this.dbTableData.find(t=>t.type == 'main')
return table.columns
}
},
methods:{
initData(){
this.lr_loadDblink()
},
async dbSelect(list,showLoading, hideLoading){
showLoading()
const tableList = []
const notAddTable = []
list.forEach(item => {
const table = {id:item.f_TableName,name:item.f_TableName,comment:item.f_Description,columns:[]}
//
if(this.tableColumns[table.name]){
table.columns = this.tableColumns[table.name]
}
else{
tableList.push(table)
}
if(this.isAddDbTableRData){
if(this.dbTableData.find(t => { return t.name == table.name }) == undefined){
this.dbTableRData.push(table)
}
else{
notAddTable.push(table.name)
}
}
else{
if(this.dbTableData.length == 0){
table.type = 'main'
this.dbTableData.push(table)
}
else if(this.dbTableData.find(t => { return t.name == table.name }) == undefined){
table.type = 'chlid'
this.dbTableData.push(table)
}
else{
notAddTable.push(table.name)
}
}
})
if(tableList.length > 0 ){
const codeTables = await this.$awaitWraper(apiCodeTable.getList(this.formData.f_DbCode,String(tableList.map(t=>t.name))))
for(const tableItem of tableList){
const codeTable = codeTables.find(t=>t.lr_db_codetableEntity.f_TableName == tableItem.name)
tableItem.columns = codeTable.lr_db_codecolumnsList.map(t=>({
name:t.f_DbColumnName,
csType:t.f_CsType,
isIdentity:t.f_IsIdentity == 1,
isPrimary:t.f_IsPrimaryKey == 1,
isNullable:t.f_IsNullable == 1,
coment:t.f_Description
}))
this.tableColumns[tableItem.name] = tableItem.columns
}
}
if(notAddTable.length > 0){
if(this.isAddDbTableRData){
this.$message({
message: `不能是表单已经绑定的表【${String(notAddTable)}`,
type: 'warning'
})
}
else{
this.$message({
message: `重复添加表【${String(notAddTable)}`,
type: 'warning'
})
}
}
this.dbtableSelectdialog = false
hideLoading()
},
chlidTagClick(row){
this.dbTableData.find(t => { return t.type == 'main' }).type = 'chlid';
row.type = 'main';
this.$set(this.dbTableData, 0, this.dbTableData[0])
},
addRow(){
this.isAddDbTableRData = false
if(this.formData.f_DbCode){
this.dbtableSelectdialog = true;
}
else{
this.$message({
message: '请选择数据库',
type: 'warning'
})
}
},
deleteRow(data){
this.dbTableData.splice(data.index,1);
if(data.row.type == 'main' && this.dbTableData.length > 0){
this.dbTableData[0].type = 'main';
this.$set(this.dbTableData, 0, this.dbTableData[0]);
}
},
relationTables(myName){
let list = [];
this.dbTableData.forEach(item =>{
if(item.name != myName){
list.push(item);
}
})
return list;
},
relationTableFields(tableName){
const table = this.dbTableData.find(t => { return t.name == tableName }) || {}
return table.columns || []
},
addDbTableRData(){
this.isAddDbTableRData = true
if(this.formData.f_DbCode){
this.dbtableSelectdialog = true
}
else{
this.$message({
message: '请选择数据库',
type: 'warning'
});
}
},
deleteDbTableRData(data){
this.dbTableRData.splice(data.index,1)
},
validateSteps(){
return new Promise((resolve) => {
if(this.stepActive == 0){
//
this.validateBaseInfo().then(res=>{
resolve(res)
})
}
else{
resolve(true)
}
});
},
steps(num){
return this.stepActive == num
},
validateBaseInfo(){
return new Promise((resolve) => {
this.$refs.baseInfo.validate((valid) => {
if(valid){
if(this.isNotFetchTables){
this.$message({
type: 'error',
message: '请将数据表的对象导入,点击数据表添加按钮,导入表重新编辑页面!'
})
resolve(false)
return
}
if(this.dbTableData.length > 0){
if(this.formData.f_FormType != 1){
if(this.dbTableData.find(t=>t.type != 'main' && (this.$validatenull(t.field) || this.$validatenull(t.relationName) || this.$validatenull(t.relationField))) != undefined){
this.$message({
type: 'error',
message: '请完善数据库表信息!'
})
resolve(false)
return
}
}
else{
if(this.dbTableData.find(t=>t.type != 'main' && this.$validatenull(t.relationField)) != undefined){
this.$message({
type: 'error',
message: '请完善子试图和主视图关联!'
});
resolve(false)
return
}
}
}
else{
this.$message({
type: 'error',
message: this.formData.f_FormType != 1? '请添加数据库表!':'请添加视图语句'
});
resolve(false)
return
}
if(this.dbTableRData.length > 0){
if(this.dbTableRData.find(t=>this.$validatenull(t.fname) || this.$validatenull(t.cfield) || this.$validatenull(t.field) || this.$validatenull(t.relationName) || this.$validatenull(t.relationField)) != undefined){
this.$message({
type: 'error',
message: '请完善数据库关联表信息!'
});
resolve(false)
return
}
}
this.$nextTick(()=>{
this.$refs.formDesign.updateTable()
})
resolve(true)
}
else{
resolve(false)
}
})
})
},
resetForm(){
this.$refs.formDesign.clear()
this.$formClear(this.$refs.baseInfo)
this.isNotFetchTables = false
this.dbTableData = []
this.dbTableRData = []
this.tableColumns = {}
},
validateForm(){
return this.$refs.formDesign.validate()
},
async setForm(id){
const data = await this.$awaitWraper(apiScheme.get(id))
if(data){
let scheme = JSON.parse(data.scheme.f_Scheme)
data.info.f_DbCode = scheme.dbCode
this.formData = data.info
this.dbTableData = scheme.db
this.dbTableRData = scheme.rdb || []
if(this.formData.f_FormType != 1){
let tableNames = []
tableNames.push(...this.dbTableData.map(t=>t.name))
tableNames.push(...this.dbTableRData.map(t=>t.name))
tableNames = this.$unique(tableNames)
//
const codeTables = await this.$awaitWraper(apiCodeTable.getList(scheme.dbCode,String(tableNames)))
//
//
if(codeTables.length < tableNames.length ){
//
this.isNotFetchTables = true
}
else{
const tableList = [...this.dbTableData,...this.dbTableRData]
for(const table of tableList){
const codeTable = codeTables.find(t=>t.lr_db_codetableEntity.f_TableName == table.name)
table.columns = codeTable.lr_db_codecolumnsList.map(t=>({
name:t.f_DbColumnName,
csType:t.f_CsType,
isIdentity:t.f_IsIdentity == 1,
isPrimary:t.f_IsPrimaryKey == 1,
isNullable:t.f_IsNullable == 1,
coment:t.f_Description
}))
this.tableColumns[table.name] = table.columns
}
}
}
this.$refs.formDesign.setData(scheme.formInfo)
return true
}
else{
this.$message({
message: '数据加载失败',
type: 'warning'
})
return false
}
},
getForm(isDraft){
const db = []
const rdb = []
let primaryKey = ''
if(this.formData.f_FormType != 1){
//
const mainTable = this.dbTableData.find(t=>t.type == 'main')
const primaryKeyObj = mainTable.columns.find(t=>t.isPrimary)
if(primaryKeyObj){
primaryKey = primaryKeyObj.name
}
}
const dbTableData = this.$deepClone(this.dbTableData)
const dbTableRData = this.$deepClone(this.dbTableRData)
for(const item of dbTableData){
if(this.formData.f_FormType != 1){
delete item.columns
}
db.push(item)
}
for(const item of dbTableRData){
if(this.formData.f_FormType != 1){
delete item.columns
}
rdb.push(item)
}
let scheme = {
dbCode:this.formData.f_DbCode,
db:db,
rdb:rdb,
primaryKey:primaryKey,
formInfo:this.$refs.formDesign.getData(),
formType:this.formData.f_FormType
}
let postData = {
info:this.$deepClone(this.formData),
scheme:{
F_Scheme:JSON.stringify(scheme),
F_Type:isDraft?2:1
}
}
return postData
},
handleFormTypeChange(){
this.dbTableData = []
this.dbTableRData = []
},
addSqlData(){//
if(this.formData.f_DbCode){
this.editSql = false
this.sqlDialogTitle = '添加SQL'
this.addSqlDialog = true
}
else{
this.$message({
message: '请选择数据库',
type: 'warning'
});
}
},
editSqlData(row){
if(this.formData.f_DbCode){
this.editSql = true
this.sqlDialogRow = row
this.sqlDialogTitle = '编辑SQL'
this.addSqlDialog = true
}
else{
this.$message({
message: '请选择数据库',
type: 'warning'
});
}
},
deleteSqlData({index,row}){//
if(row.type != 'main'){
this.dbTableData.splice(index,1)
}
else{
this.$message({
message: '主语句无法删除',
type: 'warning'
})
}
},
async sqlDialogSave(showLoading,hideLoading){
showLoading('保存中...')
if(await this.$refs.sqlForm.validateForm()){
const data = this.$refs.sqlForm.getForm()
if(this.dbTableData.length != 0 && data.type != 'main'){
if(data.sql.indexOf('@param') == -1){
this.$message({
message: '请在语句中设置关联参数',
type: 'warning'
})
hideLoading()
return
}
}
//
const list = await this.$awaitWraper(apiScheme.geColnames(this.formData.f_DbCode,data.sql))
if(list == null){
hideLoading()
return
}
data.columns = list.map(t=>{ return {name:t}})
if(this.editSql){
const index = this.dbTableData.findIndex(t=>t.id = data.id)
this.$set(this.dbTableData, index, data)
}
else{
if(this.dbTableData.length == 0){
data.type = 'main'
}
else{
data.type = 'chlid'
}
data.id = this.$uuid()
this.dbTableData.push(data)
}
this.addSqlDialog = false
}
hideLoading()
},
sqlDialogClosed(){
this.$refs.sqlForm.resetForm()
},
sqlDialogOpened(){
if(this.editSql){
this.$refs.sqlForm.setForm(this.sqlDialogRow)
}
else{
this.$refs.sqlForm.resetSql()
}
}
}
}
</script>

View File

@ -0,0 +1,60 @@
<template>
<BasicTable @register="registerContanctTable">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction :actions="[
{
label: '删除',
color: 'error',
popConfirm: {
title: '是否删除该数据',
confirm: handleDelete.bind(null, record),
},
}
]" />
</template>
</template>
</BasicTable>
</template>
<script lang="ts" setup>
import { defineProps } from 'vue';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { columns } from './databasetable.data';
let props = defineProps(["tabList"]);
console.log('props',props)
const [registerContanctTable, { reload, setTableData, getColumns }] = useTable({
title: '',
rowKey: 'id',
columns,
formConfig: {
labelWidth: 120,
},
useSearchForm: false,
showTableSetting: false,
bordered: false,
pagination: false,
actionColumn: {
width: 80,
title: '操作',
dataIndex: 'action',
fixed: undefined,
},
});
</script>
<style lang="less">
.form-box{
width: 100%;
height: calc(100% - 55px);
position:fixed;
top:55px;
left: 0;
}
</style>

View File

@ -0,0 +1,78 @@
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:canFullscreen="false"
:defaultFullscreen="false"
:maskClosable="false"
:width="800"
title="数据对象选择"
@ok="modalSuReClick"
>
<BasicTable @register="registerDataTable">
</BasicTable>
</BasicModal>
</template>
<script lang="ts" setup>
import { columns } from './dataobject.data';
import { BasicTable, useTable } from '@/components/Table';
import { getDataBaseTableList } from '@/api/formdesign/index';
import { BasicModal, useModalInner } from '/@/components/Modal';
const emit = defineEmits(['backrows']);
const [registerModal, { closeModal, setModalProps }] = useModalInner();
const [registerDataTable, { reload, getSelectRows }] = useTable({
title: '',
rowKey: 'f_Id',
api: getDataBaseTableList,
columns,
size: 'small',
rowSelection: {//
type: 'checkbox',
// type: 'radio',
},
useSearchForm: false,
showTableSetting: false,
bordered: true,
pagination:{
pageSize: 10
},
beforeFetch: (data) => {
//
var temp = {
page: data.page,
rows: data.limit,
keyword: data.key,
dbCode: "lrsystemdb"
};
return temp;
},
afterFetch: (data) => {
}
});
function modalSuReClick(){
let rows = getSelectRows();
emit('backrows', rows);
closeModal()
}
</script>
<style lang="less">
</style>

View File

@ -0,0 +1,100 @@
import { BasicColumn, FormSchema } from '@/components/Table';
import { getOutKeyList } from '@/api/formdesign/index'
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
let tabList
export function sendtabledata(data:any[]) {
console.log('aaad',data)
tabList = data
}
export const columns: BasicColumn[] = [
{
title: '名称',
dataIndex: 'status',
width: 100,
editComponent: 'Select',
editable: true,
edit: true,
},
{
title: '表名',
dataIndex: 'f_TableName',
width: 120,
},
{
title: '外键',
editComponent: 'ApiSelect',
width: 180,
dataIndex: 'field',
editable: true,
edit: true,
editComponentProps: ({ record }) => {
return {
api: () => getOutKeyList({tableNames: record.f_TableName}).then((data: AreaRespVO[]) => {
return data[0].lr_db_codecolumnsList
}),
params: {},
// 接口参数
resultField: 'data',
labelField: 'f_DbColumnName',
valueField: 'f_DbColumnName',
};
}
},
{
title: '关联表',
dataIndex: 'relationName',
editComponent: 'Select',
width: 180,
editable: true,
edit: true,
editComponentProps: ({ record }) => {
let arr = []
tabList.forEach(item =>{
if(item.f_TableName !== record.f_TableName){
arr.push({
label: item.f_TableName,
value: item.f_TableName,
})
}
})
return {
options: arr,
onChange: (e: any) => {
tabSelectId = e
}
}
},
},
{
title: '关联主键',
editComponent: 'ApiSelect',
width: 180,
dataIndex: 'relationField',
editable: true,
edit: true,
editComponentProps: ({ record }) => {
return {
api: () => getOutKeyList({tableNames: record.f_TableName}).then((data: AreaRespVO[]) => {
return data[0].lr_db_codecolumnsList
}),
params: {},
// 接口参数
resultField: 'data',
labelField: 'f_DbColumnName',
valueField: 'f_DbColumnName',
};
},
},
];

View File

@ -0,0 +1,25 @@
import { BasicColumn } from '@/components/Table';
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
export const columns: BasicColumn[] = [
{
title: '表名',
dataIndex: 'f_TableName',
},
{
title: '备注',
dataIndex: 'f_Description'
},
{
title: '状态',
dataIndex: 'f_State'
}
];

View File

@ -0,0 +1,213 @@
import { BasicColumn, FormSchema } from '@/components/Table';
import { optionsListApi,getOutKeyList } from '@/api/formdesign/index'
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
let tabList
let tabSelectId
export function sendtabledata(data:any[]) {
tabList = data
}
export const columns: BasicColumn[] = [
{
title: '类别',
dataIndex: 'status',
width: 50,
customRender: ({ record }) => {
const status = record.type;
let enable
if(status == "main"){
enable = true
}else{
enable = false
}
const color = enable ? '#67c23a' : '#e6a23c';
const text = enable ? '主表' : '子表';
return h(Tag, { color: color }, () => text);
},
},
{
title: '表名',
dataIndex: 'f_TableName',
width: 120,
},
{
title: '外键',
editComponent: 'ApiSelect',
width: 180,
dataIndex: 'field',
editable: true,
edit: true,
editComponentProps: ({ record }) => {
return {
api: () => getOutKeyList({tableNames: record.f_TableName}).then((data: AreaRespVO[]) => {
return data[0].lr_db_codecolumnsList
}),
params: {},
// 接口参数
resultField: 'data',
labelField: 'f_DbColumnName',
valueField: 'f_DbColumnName',
};
}
},
{
title: '关联表',
dataIndex: 'relationName',
editComponent: 'Select',
width: 180,
editable: true,
edit: true,
editComponentProps: ({ record }) => {
let arr = []
tabList.forEach(item =>{
if(item.f_TableName !== record.f_TableName){
arr.push({
label: item.f_TableName,
value: item.f_TableName,
})
}
})
return {
options: arr,
onChange: (e: any) => {
tabSelectId = e
}
}
},
},
{
title: '关联主键',
editComponent: 'ApiSelect',
width: 180,
dataIndex: 'relationField',
editable: true,
edit: true,
editComponentProps: ({ record }) => {
return {
api: () => getOutKeyList({tableNames: record.f_TableName}).then((data: AreaRespVO[]) => {
return data[0].lr_db_codecolumnsList
}),
params: {},
// 接口参数
resultField: 'data',
labelField: 'f_DbColumnName',
valueField: 'f_DbColumnName',
};
},
},
];
export const formSchema: FormSchema[] = [
{
field: 'f_Name',
component: 'Input',
label: '名称',
colProps: {
span: 24,
},
defaultValue: '',
rules: [{ required: true }],
componentProps: {
placeholder: '请输入',
onChange: (e) => {
console.log(e);
},
},
},
{
field: 'f_Category',
component: 'ApiSelect',
label: '分类',
required: true,
colProps: {
span: 24,
},
componentProps: ({ formActionType, formModel }) => {
return {
api: optionsListApi, // 接口
// 接口参数
resultField: 'data',
labelField: 'f_ItemName',
valueField: 'f_ItemId',
};
},
},
{
field: 'f_DbCode',
component: 'ApiSelect',
label: '数据库',
required: true,
colProps: {
span: 24,
},
componentProps: {
options: [
{ label: '系统数据库', value: 'lrsystemdb' },
],
},
},
{
field: 'f_EnabledMark',
label: '状态',
component: 'RadioButtonGroup',
defaultValue: 1,
componentProps: {
options: [
{ label: '是', value: 1 },
{ label: '否', value: 0 },
],
},
},
{
field: 'f_FormType',
label: '类型',
component: 'ApiRadioGroup',
defaultValue: '0',
componentProps: {
options: [
{ label: '常规表单', value: '0' },
{ label: '视图表单', value: '1' },
],
},
colProps: { lg: 24, md: 24 },
},
{
field: 'f_Description',
component: 'InputTextArea',
label: '描述',
colProps: {
span: 24,
},
defaultValue: '',
componentProps: {
placeholder: '请输入',
onChange: (e) => {
console.log(e);
},
},
},
{
field: 'field1',
label: '',
colProps: {
span: 24,
},
slot: 'addDatabaseTableSlot',
},
{
field: 'field1',
label: '',
colProps: {
span: 24,
},
slot: 'addDatabaseContantSlot',
},
]

View File

@ -0,0 +1,160 @@
<template>
<div class="l-rblock" >
<div class="l-page-pane">
<BasicForm
ref="myDataBaseFormRef"
@register="registerForm"
>
<template #addDatabaseTableSlot="{ model, field }">
<div>添加数据库表请先选择数据库</div>
<BasicTable @register="registerDataBaseTable">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction :actions="[
{
label: '删除',
color: 'error',
popConfirm: {
title: '是否删除该数据',
confirm: handleDelete.bind(null, record),
},
}
]" />
</template>
</template>
</BasicTable>
<div class="addDataBaseTableBox" @click="handleAddDataBase">
<a-button size="small" type="link"><template #icon><PlusOutlined /></template>添加</a-button>
</div>
</template>
<template #addDatabaseContantSlot="{ model, field }">
<div>添加数据库关联表只用来做数据权限设置请先选择数据库</div>
<DataBaseTable :tabList="tabList" key="databaseKey"></DataBaseTable>
<div class="addDataBaseTableBox" @click="handleAddDataBase">
<a-button size="small" type="link"><template #icon><PlusOutlined /></template>添加</a-button>
</div>
</template>
</BasicForm>
</div>
</div>
<DataObject @register="registerModal" @backrows="handleBackRows" />
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { BasicForm, useForm } from '/@/components/Form';
import { formSchema , columns , sendtabledata } from './index.data';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { BasicModal , useModal , useModalInner } from '/@/components/Modal';
import DataObject from './DataObject.vue'
import DataBaseTable from './DataBaseTable.vue'
const [registerModal, { openModal }] = useModal();
let tabList: any[] = []
let tableColumns: any[] = {}
let isAddDbTableRData: any[] = false
let dbTableData: any[] = []
let databaseKey: any[] = 0
const myDataBaseFormRef = ref<any>();
const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
labelWidth: 100,
schemas: formSchema,
showActionButtonGroup: false,
baseColProps: { lg: 24, md: 24 },
});
const [registerDataBaseTable, { reload, setTableData, getColumns }] = useTable({
title: '',
rowKey: 'id',
columns,
formConfig: {
labelWidth: 120,
},
useSearchForm: false,
showTableSetting: false,
bordered: false,
pagination: false,
actionColumn: {
width: 80,
title: '操作',
dataIndex: 'action',
fixed: undefined,
},
});
function handleAddDataBase() {
const anyformobj = ref<any>(myDataBaseFormRef.value.getFieldsValue());
if(anyformobj.value.f_DbCode){
openModal(true, {});
}else{
message.warning("请选择数据库")
}
}
function handleDelete(record: Recordable) {
// openAccountModal(true, {
// record,
// });
}
function handleBackRows(ModuleId = '') {
ModuleId.forEach(item =>{
tabList.push(item)
})
tabList.forEach((item,index) =>{
if(index == 0){
item.type = "main"
}else{
item.type = 'child'
}
})
setTableData(tabList)
reload()
databaseKey++
console.log('databaseKey',databaseKey)
const chart1data1 = sendtabledata(tabList)
}
</script>
<style lang="less">
.l-rblock{
width: 100%;
height: 100%;
background:#f0f2f5;
padding: 24px;
}
.l-page-pane{
box-sizing: border-box;
position: relative;
width: 100%;
height: 100%;
max-width: 794px;
overflow: hidden auto;
background: #fff;
border-radius: 4px;
margin: auto;
padding: 24px;
}
.addDataBaseTableBox{
border: 1px dashed #f0f0f0;
text-align: center;
cursor: pointer;
margin-top: -20px;
&:hover {
border-color: #409EFF;
}
}
</style>

View File

@ -0,0 +1,181 @@
<template>
<lrlayout class="l-tab-page">
<template #left>
<lrPanel style="padding-right:0;" >
<template #title>
{{$t('分类')}}
<div class="tree-setting-btn">
<Tooltip placement="top" title="设置">
<span><SettingOutlined style="color:blue" /></span>
</Tooltip>
</div>
</template>
<BasicTree ref="asyncExpandTreeRef"
treeWrapperClassName="h-[calc(100%-35px)] overflow-auto" loadData
:clickRowToExpand="false" :treeData="treeData" :fieldNames="{ key: 'f_ItemId', title: 'f_ItemName' }"
:defaultExpandAll="true" @select="handleSelect" />
</lrPanel>
</template>
<BasicTable @register="registerTable" :searchInfo="searchInfo">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction :actions="[
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
color: 'error',
popConfirm: {
title: '是否删除该数据',
confirm: handleDelete.bind(null, record),
},
}
]" />
</template>
</template>
<template #toolbar>
<a-button type="primary" @click="handleAddForm"> </a-button>
</template>
</BasicTable>
<FormModal @register="registerModal" />
</lrlayout>
</template>
<script lang="ts" setup>
import { onMounted, ref, nextTick, unref,reactive } from 'vue';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { getFormGroupList } from '@/api/formdesign/index';
import { optionsListApi } from '@/api/formdesign/index';
import { SettingOutlined } from '@ant-design/icons-vue';
import lrlayout from '@/components/lrLayout';
import lrPanel from '@/components/lrPanel';
import FormModal from './FormModal.vue';
import { BasicTree, TreeItem, TreeActionItem } from '@/components/Tree';
import { useModal } from '/@/components/Modal';
import { columns, searchFormSchema } from './form.data';
const [registerModal, { openModal }] = useModal();
const [registerTable, { reload, getSelectRows }] = useTable({
title: '表单列表',
api: getFormGroupList,
rowKey: 'f_Id',
columns,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
},
useSearchForm: true,
showTableSetting: true,
bordered: true,
beforeFetch: (data) => {
//
var temp = {
page: data.page,
rows: data.limit,
keyword: data.key,
category: data.f_Category
};
return temp;
},
afterFetch: (data) => {
data.forEach(item =>{
let d = item.f_CreateDate ? new Date(item.f_CreateDate) : new Date(),
obj = {
year: d.getFullYear(),
month: d.getMonth() + 1,
day: d.getDate(),
hours: d.getHours(),
min: d.getMinutes(),
seconds: d.getSeconds()
}
Object.keys(obj).forEach(key => {
if (obj[key] < 10) obj[key] = `0${obj[key]}`
})
item.f_CreateDate = `${obj.year}-${obj.month}-${obj.day} ${obj.hours}:${obj.min}:${obj.seconds}`
item.f_CreateDate = item.f_CreateDate.replace(/T/g, ' ').replace(/.[\d]{3}Z/, ' ')
treeData.value.forEach(val =>{
if(item.f_Category == val.f_ItemValue){
item.f_Category_Name = val.f_ItemName
}
})
})
//
},
handleSearchInfoFn(info) {
return info;
},
actionColumn: {
width: 180,
title: '操作',
dataIndex: 'action',
// slots: { customRender: 'action' },
fixed: undefined,
},
});
const searchInfo = reactive < Recordable > ({});
const treeData = ref < TreeItem[] > ([]);
const asyncExpandTreeRef = ref < Nullable < TreeActionType >> (null);
async function fetch() {
treeData.value = (await optionsListApi()) as unknown as TreeItem[];
//
nextTick(() => {
unref(asyncExpandTreeRef)?.expandAll(true);
});
}
function handleSelect(keys) {
treeData.value.forEach(item =>{
if(keys[0] == item.f_ItemId){
searchInfo.f_Category = item.f_Category;
}
})
reload();
}
function handleEdit(record: Recordable) {
// openAccountModal(true, {
// record,
// });
}
function handleDelete(record: Recordable) {
// openAccountModal(true, {
// record,
// });
}
function handleAddForm() {
openModal(true, {});
}
onMounted(() => {
fetch();
})
</script>

View File

@ -0,0 +1,198 @@
import { BasicColumn, FormSchema } from '@/components/Table';
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
import Icon from '@/components/Icon/Icon.vue';
export const columns: BasicColumn[] = [
{
title: '按钮名称',
dataIndex: 'name',
},
{
title: 'DOMID',
dataIndex: 'domId',
},
{
title: '排序',
dataIndex: 'sort',
},
];
const isDir = (type: string) => type === '0';
const isMenu = (type: string) => type === '1';
const isButton = (type: string) => type === '2';
export const searchFormSchema: FormSchema[] = [
{
field: 'key',
label: '关键字',
component: 'Input',
colProps: { span: 8 },
},
];
export const formSchema: FormSchema[] = [
{
field: 'type',
label: '类型',
component: 'RadioButtonGroup',
defaultValue: '1',
componentProps: {
options: [
// { label: '目录', value: '0' },
{ label: '菜单', value: '1' },
{ label: '按钮', value: '2' },
],
},
colProps: { lg: 24, md: 24 },
},
{
field: 'id',
label: '名称',
component: 'Input',
ifShow:false
},
{
field: 'name',
label: '名称',
component: 'Input',
required: true,
},
{
field: 'parentId',
label: '上级',
component: 'TreeSelect',
componentProps: {
fieldNames: {
label: 'name',
key: 'id',
value: 'id',
},
getPopupContainer: () => document.body,
},
ifShow: ({ values }) => !isButton(values.type),
},
{
field: 'moduleId',
label: '菜单',
component: 'TreeSelect',
componentProps: {
fieldNames: {
label: 'name',
key: 'id',
value: 'id',
},
getPopupContainer: () => document.body,
},
ifShow: ({ values }) => isButton(values.type),
required: true,
},
{
field: 'domId',
label: 'DMOID',
component: 'Input',
required: true,
ifShow: ({ values }) => isButton(values.type),
},
{
field: 'sortNo',
label: '排序',
component: 'InputNumber',
ifShow: ({ values }) => !isButton(values.type),
},
{
field: 'sort',
label: '排序',
component: 'InputNumber',
ifShow: ({ values }) => isButton(values.type),
},
{
field: 'class',
helpMessage: ['参考参数值', 'success、warning、error'],
label: '样式',
component: 'Input',
ifShow: ({ values }) => isButton(values.type),
},
{
field: 'iconName',
label: '图标',
component: 'IconPicker',
ifShow: ({ values }) => !isButton(values.type),
},
{
field: 'url',
label: '路由地址',
component: 'Input',
required: true,
ifShow: ({ values }) => !isButton(values.type),
},
// {
// field: 'component',
// label: '组件路径',
// component: 'Input',
// ifShow: ({ values }) => isMenu(values.type),
// },
{
field: 'code',
label: '权限标识',
component: 'Input',
ifShow: ({ values }) => !isButton(values.type),
},
{
field: 'status',
label: '是否系统',
component: 'RadioButtonGroup',
defaultValue: 1,
componentProps: {
options: [
{ label: '是', value: 0 },
{ label: '否', value: 1 },
],
},
ifShow: ({ values }) => !isButton(values.type),
},
// {
// field: 'isExt',
// label: '是否外链',
// component: 'RadioButtonGroup',
// defaultValue: '0',
// componentProps: {
// options: [
// { label: '否', value: '0' },
// { label: '是', value: '1' },
// ],
// },
// ifShow: ({ values }) => !isButton(values.type),
// },
// {
// field: 'keepalive',
// label: '是否缓存',
// component: 'RadioButtonGroup',
// defaultValue: '0',
// componentProps: {
// options: [
// { label: '否', value: '0' },
// { label: '是', value: '1' },
// ],
// },
// ifShow: ({ values }) => isMenu(values.type),
// },
// {
// field: 'show',
// label: '是否显示',
// component: 'RadioButtonGroup',
// defaultValue: '0',
// componentProps: {
// options: [
// { label: '是', value: '0' },
// { label: '否', value: '1' },
// ],
// },
// ifShow: ({ values }) => !isButton(values.type),
// },
];

View File

@ -52,7 +52,6 @@
const values = await validate();
setDrawerProps({ confirmLoading: true });
// TODO custom api
console.log(values)
if (values.type == '1') {
console.log("新增菜单")
delete values.type

View File

@ -117,7 +117,6 @@
nextTick(expandAll);
}
function onBtnClicked(domId) {
console.log(domId)
switch (domId) {
case 'btnAdd':
handleCreate()

View File

@ -74,6 +74,7 @@
});
}
async function editGroup(record: Recordable) {
console.log('record',record)
openDrawer(true, {
record,
isUpdate: true,