Compare commits
3 Commits
49a92b86bd
...
6431118316
| Author | SHA1 | Date |
|---|---|---|
|
|
6431118316 | |
|
|
f8d0ea1966 | |
|
|
e54b264bd4 |
|
|
@ -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
|
|
@ -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 });
|
||||
|
|
@ -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>;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import lrLayout from './src/lrLayout.vue'
|
||||
|
||||
lrLayout.install = function(Vue) {
|
||||
Vue.component(lrLayout.name, lrLayout)
|
||||
}
|
||||
|
||||
export default lrLayout
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import lrPanel from './src/lrPanel.vue'
|
||||
|
||||
lrPanel.install = function(Vue) {
|
||||
Vue.component(lrPanel.name, lrPanel)
|
||||
}
|
||||
|
||||
export default lrPanel
|
||||
|
|
@ -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>
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ async function getAsyncMenus() {
|
|||
}
|
||||
if (isRouteMappingMode()) {
|
||||
// 333333
|
||||
console.log(permissionStore.getFrontMenuList)
|
||||
return menuFilter(permissionStore.getFrontMenuList);
|
||||
}
|
||||
return staticMenus;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 = {
|
|||
}
|
||||
// 这里 code,result,message为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
|
||||
const { code, result, message } = data;
|
||||
|
||||
// 这里逻辑可以根据项目进行修改
|
||||
const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS;
|
||||
if (hasSuccess) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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}` });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
// 不进行任何处理,直接返回
|
||||
// 用于页面代码可能需要直接获取code,data,message这些信息时开启
|
||||
if (!isTransformResponse) {
|
||||
return res.data;
|
||||
}
|
||||
// 错误的时候返回
|
||||
|
||||
const { data } = res;
|
||||
if (!data) {
|
||||
// return '[HTTP] Request has no return value';
|
||||
throw new Error(t('sys.api.apiRequestFailed'));
|
||||
}
|
||||
// 这里 code,result,message为 后台统一的字段,需要在 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 schemes,e.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',
|
||||
// },
|
||||
// });
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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 },
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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',
|
||||
};
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
|
@ -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'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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),
|
||||
// },
|
||||
];
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -117,7 +117,6 @@
|
|||
nextTick(expandAll);
|
||||
}
|
||||
function onBtnClicked(domId) {
|
||||
console.log(domId)
|
||||
switch (domId) {
|
||||
case 'btnAdd':
|
||||
handleCreate()
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@
|
|||
});
|
||||
}
|
||||
async function editGroup(record: Recordable) {
|
||||
console.log('record',record)
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
|
|
|
|||
Loading…
Reference in New Issue