master
xy 2 years ago
parent 85a050cc0f
commit 20bcb15194

@ -1,6 +1,6 @@
# just a flag
ENV = 'development'
ENV='development'
# base api
VUE_APP_BASE_API = http://starter.ali251.langye.net
VUE_APP_UPLOAD_API =http://starter.ali251.langye.net/api/admin/upload-file
VUE_APP_BASE_API=http://starter.ali251.langye.net
VUE_APP_UPLOAD_API=http://starter.ali251.langye.net/api/admin/upload-file

@ -20,7 +20,7 @@
"axios": "0.18.1",
"core-js": "3.6.5",
"echarts": "^4.2.1",
"element-ui": "2.13.2",
"element-ui": "2.15.13",
"js-cookie": "2.2.0",
"less-loader": "^5.0.0",
"moment": "^2.29.2",
@ -32,6 +32,7 @@
"vue": "2.6.10",
"vue-count-to": "^1.0.13",
"vue-router": "3.0.6",
"vuedraggable": "^2.24.3",
"vuex": "3.1.0"
},
"devDependencies": {

@ -24,11 +24,12 @@ export function index(params,isLoading = false) {
})
}
export function show(params) {
export function show(params, isLoading = true) {
return request({
method: "get",
url: "/api/admin/base-form/show",
params
params,
isLoading
})
}

@ -33,14 +33,6 @@ export function destroy(params) {
})
}
export function config(params) {
return request({
method: "get",
url: "/api/admin/custom-form/config",
params
})
}
export function update(params) {
return request({
method: "get",

@ -33,3 +33,20 @@ export function destroy(params) {
})
}
export function config(params,isLoading = false) {
return request({
method: "get",
url: "/api/admin/custom-form-field/config",
params,
isLoading
})
}
export function translate(params) {
return request({
method: "get",
url: "/api/admin/custom-form-field/pinyin",
params,
isLoading: false
})
}

@ -562,6 +562,20 @@ export default {
let _this = this;
if (_this.auths?.length > 0) {
let btns = new Map();
btns.set(
"detail",
<i-button
style={{
"margin-right": "6px",
}}
type="primary"
size="small"
ghost={true}
onClick={() => _this.$emit('detail', scope.row, scope)}
>
查看
</i-button>
)
btns.set(
"edit",
<i-button

@ -0,0 +1,72 @@
import {getToken} from "@/utils/auth";
export const addPropsMap = new Map([
["text", {
clearable: true
}],
["richtext",{}],
['radio',{
}],
["checkbox", {
}],
["date", {
clearable: true,
valueFormat: 'yyyy-MM-dd'
}],
["datetime", {
type: 'datetime',
clearable: true,
valueFormat: 'yyyy-MM-dd mm:hh:ss',
pickerOptions: {
shortcuts: [{
text: '今天',
onClick(picker) {
picker.$emit('pick', new Date());
}
}, {
text: '昨天',
onClick(picker) {
const date = new Date();
date.setTime(date.getTime() - 3600 * 1000 * 24);
picker.$emit('pick', date);
}
}, {
text: '一周前',
onClick(picker) {
const date = new Date();
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', date);
}
}]
}
}],
["file",{
action: process.env.VUE_APP_UPLOAD_API,
headers: {
Authorization: `Bearer ${getToken()}`,
},
autoUpload: false,
multiple: false,
limit: 1
}],
["files",{
action: process.env.VUE_APP_UPLOAD_API,
headers: {
Authorization: `Bearer ${getToken()}`,
},
autoUpload: false,
multiple: true
}],
["textarea", {
clearable: true,
type: 'textarea',
autosize: {
'minRows': 2
}
}],
["map", "el-input"],
])

@ -1,108 +1,14 @@
export default [
{
label: "element-ui",
options: [
{
value: "el-radio",
label: "单选框",
},
{
value: "el-checkbox",
label: "多选框",
},
{
value: "text",
label: "输入框",
},
{
value: "el-input-number",
label: "计数器",
},
{
value: "el-select",
label: "选择器"
},
{
value: "el-switch",
label: "开关",
},
{
value: "el-slider",
label: "滑块",
},
{
value: "el-time-select",
label: "时间选择器",
},
{
value: "el-date-picker",
label: "日期选择器",
},
{
value: "el-upload",
label: "上传",
},
{
value: "el-rate",
label: "评分",
},
{
value: "el-color-picker",
label: "颜色选择器",
},
],
},
{
label: "iview",
options: [
{
value: "Input",
label: "输入框",
},
{
value: "Radio",
label: "单选框",
},
{
value: "Checkbox",
label: "多选框",
},
{
value: "Switch",
label: "开关",
},
{
value: "select",
label: "选择器",
},
{
value: "Slider",
label: "滑块",
},
{
value: "DatePicker",
label: "日期选择器",
},
{
value: "TimeSelect",
label: "时间选择器",
},
{
value: "InputNumber",
label: "计数器",
},
{
value: "Upload",
label: "上传",
},
{
value: "Rate",
label: "评分",
},
{
value: "ColorPicker",
label: "颜色选择器",
},
],
},
];
export const domMap = new Map([
["text", "el-input"],
["richtext",'my-tinymce'],
['radio','el-radio-group'],
["checkbox", "el-checkbox-group"],
["date", "el-date-picker"],
["datetime", "el-date-picker"],
["file","el-upload"],
["files","el-upload"],
["textarea", "el-input"],
["map", "el-input"],
])

@ -0,0 +1,49 @@
export const templatePropsMap = new Map([
["text", {
readonly: true,
clearable: true
}],
["richtext",'my-tinymce'],
['radio',{
value: '选项1'
}],
["checkbox", {
value: ['选项1']
}],
["date", {
readonly: true,
}],
["datetime", {
readonly: true,
}],
["file",{
action: "a",
fileList: [
{name: 'food.jpeg', url: ''}
],
autoUpload: false,
multiple: false,
limit: 1
}],
["files",{
action: "a",
fileList: [
{name: 'food.jpeg', url: ''},
{name: 'food2.jpeg', url: ''}
],
autoUpload: false,
multiple: true
}],
["textarea", {
readonly: true,
clearable: true,
type: 'textarea',
autosize: {
'minRows': 2
}
}],
["map", "el-input"],
])

@ -70,6 +70,8 @@ import XyDialog from '@/components/XyDialog'
Vue.component('xy-dialog',XyDialog)
import XySelectors from '@/components/XySelectors'
Vue.component('xy-selectors',XySelectors)
import draggable from 'vuedraggable';
Vue.component('draggable',draggable)
Vue.prototype.$integrateData = (target,value) => {
for(let i in target){

@ -5,6 +5,7 @@ import app from './modules/app'
import permission from './modules/permission'
import settings from './modules/settings'
import user from './modules/user'
import form from './modules/form'
Vue.use(Vuex)
@ -13,7 +14,8 @@ const store = new Vuex.Store({
app,
permission,
settings,
user
user,
form
},
getters
})

@ -0,0 +1,102 @@
import { save, index, destroy } from "@/api/system/customFormField";
import { Message } from "element-ui";
const state = {
formList: [], //更个表单配置信息
copyFormListId: [], //备份原始的字段id数组,以做删除
selectedForm: null, //当前编辑的表单字段配置
selectedIndex: null,
};
const mutations = {
SET_COPY_FORM_LIST_ID: (state, arr) => {
state.copyFormListId = arr;
},
SET_SELECTED_INDEX: (state, index) => {
state.selectedIndex = index;
},
CLEAR_SELECTED_INDEX: (state) => {
state.selectedIndex = null;
},
SET_SELECTED: (state, value) => {
state.selectedForm = value;
},
CLEAR_SELECTED: (state, value) => {
state.selectedForm = null;
},
SET_FORM_LIST: (state, list) => {
state.formList = list;
},
SPLICE_FORM_LIST: (state, info) => {
const { index, length, config } = info;
if (config) {
state.formList.splice(index, length || 0, config || state.selectedForm);
} else {
state.formList.splice(index, length || 0);
}
},
};
const actions = {
submit: ({ state, commit }) => {
return new Promise((resolve, reject) => {
state.formList.forEach((item,index) => {
item.sort = index + 1
})
let formListId = state.formList.filter((i) => !!i.id).map((i) => i.id);
let deleteIds = state.copyFormListId.filter(
(i) => !formListId.includes(i)
);
let promiseAll = [
...state.formList.map((i) => save(i)),
...deleteIds.map((i) => destroy({ id: i })),
];
Promise.all(promiseAll)
.then((res) => {
Message({
type: "success",
message: "保存成功",
});
resolve(res);
})
.catch((err) => {
reject(err);
});
});
},
getFormList: ({ state, commit }, custom_form_id) => {
return new Promise((resolve, reject) => {
index(
{
page: 1,
page_size: 999,
sort_name: "sort",
sort_type: "asc",
custom_form_id,
},
true
)
.then((res) => {
resolve(res);
if (res?.data instanceof Array) {
commit("SET_FORM_LIST", res.data);
commit(
"SET_COPY_FORM_LIST_ID",
res.data.map((i) => i.id)
);
} else {
console.warn("res.data not Array");
}
})
.catch((err) => {
reject(err);
});
});
},
};
export default {
namespaced: true,
state,
mutations,
actions,
};

@ -1,6 +1,6 @@
import Cookies from 'js-cookie'
const TokenKey = 'sishitongtang_token'
const TokenKey = 'starter_token'
export function getToken() {
return Cookies.get(TokenKey)

@ -0,0 +1,92 @@
import axios from "axios";
import { getToken } from "@/utils/auth";
import { Loading, Message } from "element-ui";
/*
* @params {string} url 请求拼接地址
* @params {object} info 请求参数params或data
*/
let loading;
export async function download(url, method = "get", info, filename) {
loading = Loading.service({
lock: true,
background: "rgba(0,0,0,0.4)",
text: "文件正在生成中...",
});
let options = {
baseURL: process.env.VUE_APP_BASE_API,
url,
method,
responseType: "blob",
timeout: 10000,
headers: {
Accept: "application/json",
"Content-Type": "application/json; charset=utf-8",
withCredentials: true,
Authorization: "Bearer " + getToken(),
},
};
if (method === "get") {
Object.defineProperty(options, "params", {
value: info,
enumerable: true,
writable: false,
});
}
if (method === "post") {
Object.defineProperty(options, "data", {
value: info,
enumerable: true,
writable: false,
});
}
try {
const response = await axios.request(options);
loading.close();
// 提取文件名
if (!filename) {
filename =
response.headers["content-disposition"]?.match(/filename=(.*)/)[1] ||
"";
}
// 将二进制流转为blob
const blob = new Blob([response.data], {
type: "application/octet-stream",
});
if (typeof window.navigator.msSaveBlob !== "undefined") {
// 兼容IEwindow.navigator.msSaveBlob以本地方式保存文件
window.navigator.msSaveBlob(blob, decodeURI(filename));
} else {
// 创建新的URL并指向File对象或者Blob对象的地址
const blobURL = window.URL.createObjectURL(blob);
// 创建a标签用于跳转至下载链接
const tempLink = document.createElement("a");
tempLink.style.display = "none";
tempLink.href = blobURL;
tempLink.setAttribute("download", decodeURI(filename));
// 兼容某些浏览器不支持HTML5的download属性
if (typeof tempLink.download === "undefined") {
tempLink.setAttribute("target", "_blank");
}
// 挂载a标签
document.body.appendChild(tempLink);
tempLink.click();
document.body.removeChild(tempLink);
// 释放blob URL地址
window.URL.revokeObjectURL(blobURL);
}
} catch (err) {
console.error(err);
loading.close();
Message({
type: "error",
message: err,
});
}
}

@ -158,3 +158,16 @@ export function deepCopy(data) {
return data;
}
}
export function debounce(func, delay) {
let timer = null;
return function() {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(function() {
func.apply(context, args);
}, delay);
}
}

@ -1,36 +1,24 @@
<script>
import { save, show } from "@/api/system/baseForm";
import { save, show, index } from "@/api/system/baseForm";
import { getparameter } from "@/api/system/dictionary";
import { domMap } from "@/const/inputType";
import { addPropsMap } from "@/const/addProps";
export default {
props: {
formInfo: {
type: Array,
default: () => [],
},
tableName: String
tableName: String,
},
render(h) {
let domMap = new Map([
['text', 'el-input'],
['textarea', 'el-input'],
['checkbox', 'el-select'],
['date', 'el-date-picker'],
['datetime', 'el-time-select']
])
let typeMap = new Map([
['text', ''],
['textarea', 'textarea'],
['checkbox', ''],
['date', 'date'],
['datetime', '']
])
return h(
"el-dialog",
{
props: {
title: "新增",
visible: this.dialogVisible,
width: "30%",
width: "600px",
},
on: {
"update:visible": (val) => {
@ -48,50 +36,59 @@ export default {
labelWidth: "80px",
rules: this.rules,
labelPosition: "right",
size: "small"
size: "small",
},
},
this.formInfo.map((i,index) => {
return h("el-form-item",{
ref: `elFormItem${i.field}`,
props: {
label: i.name,
prop: i.field
(() => {
let dom = [];
this.formInfo.forEach((i, index) => {
if (i.list_show) {
dom.push(
h(
"el-form-item",
{
ref: `elFormItem${i.field}`,
style: {
width: "100%",
},
props: {
label: i.name,
prop: i.field,
},
},
[
h(
domMap.get(i.edit_input),
{
ref: `elEdit_${i.field}`,
props: {
...addPropsMap.get(i.edit_input),
...this.extraProps(i.edit_input),
placeholder: i.help,
value: this.form[i.field],
},
attrs: {
placeholder: i.help || `请填写${i.name}`,
},
on: {
[this.getEventType(i.edit_input)]: (e) => {
console.log(1111, e);
if (i.field) {
this.form[i.field] = e;
this.form = Object.assign({}, this.form);
}
},
},
},
this.optionsRender(h, i)
),
]
)
);
}
},[
h(domMap.get(i.edit_input),{
props: {
value: this.form[i.field],
type: typeMap.get(i.edit_input),
placeholder: `请填写${i.name}`,
valueFormat: (() => {
if(i.edit_input === 'date') {
return 'yyyy-MM-dd'
}
})(),
},
attrs: {
placeholder: `请填写${i.name}`
},
on: {
['input']:e => {
if(i.field) {
this.form[i.field] = e
this.form = Object.assign({}, this.form)
}
}
}
},(i.edit_input === 'checkbox' && i.paramters) ? i.paramters.map(param => {
return h('el-option',{
props: {
label: param.value,
value: param.id,
key: param.id
}
})
}) : '')
])
})
});
return dom;
})()
),
h("template", { slot: "footer" }, [
h(
@ -101,20 +98,20 @@ export default {
click: () => (this.dialogVisible = false),
},
},
"取消"
"取 消"
),
h(
"el-button",
{
props: {
type: 'warning',
plain: true
type: "warning",
plain: true,
},
on: {
click: () => (this.init()),
click: () => this.init(),
},
},
"重置"
"重 置"
),
h(
"el-button",
@ -123,10 +120,10 @@ export default {
type: "primary",
},
on: {
click: this.submit
click: this.submit,
},
},
"确定"
"确 定"
),
]),
]
@ -142,11 +139,114 @@ export default {
};
},
methods: {
//on
getEventType(info) {
if (info.type === "checkbox") {
return "change";
}
return "input";
},
//
optionsRender(h, info) {
if (info.edit_input === "radio") {
return info._paramters && info._paramters instanceof Array
? info._paramters.map((i) =>
h(
"el-radio",
{ props: { label: i.id } },
i.name || i.no || i.value || i.id
)
)
: [];
}
if (info.edit_input === "checkbox") {
return info._paramters && info._paramters instanceof Array
? info._paramters.map((i) =>
h(
"el-checkbox",
{ props: { label: i.id } },
i.name || i.no || i.value || i.id
)
)
: [];
}
if (info.edit_input === "file" || info.edit_input === "files") {
return [
h(
"el-button",
{
slot: "trigger",
props: {
size: "small",
type: "primary",
},
},
"选取文件"
),
h(
"el-button",
{
style: {
"margin-left": "10px",
},
props: {
size: "small",
type: "success",
},
on: {
["click"]: (e) => {
this.$refs[`elEdit_${info.field}`].submit();
},
},
},
"上传到服务器"
),
h(
"div",
{
class: "el-upload__tip",
slot: "tip",
},
"文件不超过500kb"
),
];
}
},
extraProps(info) {
let props = {};
if (info.edit_input === "file" || info.edit_input === "files") {
props.beforeUpload = (file) => {
if (file.size / 1000 > 500) {
this.$message({
type: "warning",
message: "上传图片大小超过500kb",
});
return false;
}
};
props.onSuccess = (response, file, fileList) => {
info._fileList = fileList;
};
props.onRemove = (file, fileList) => {
info._fileList = fileList;
};
}
return props;
},
init() {
for(let key in this.form) {
this.form[key] = ""
for (let key in this.form) {
if (this.form[key] instanceof Array) {
this.form[key] = [];
} else {
this.form[key] = "";
}
}
this.$refs["elForm"].clearValidate()
this.$refs["elForm"].clearValidate();
},
show() {
this.dialogVisible = true;
@ -171,7 +271,7 @@ export default {
},
async getDetail() {
const res = await show({ id: this.id,table_name: this.tableName });
const res = await show({ id: this.id, table_name: this.tableName });
this.$integrateData(this.form, res);
this.form = Object.assign({}, this.form);
},
@ -190,45 +290,58 @@ export default {
writable: true,
});
}
this.$refs['elForm'].validate(validate => {
if(validate) {
console.log(this.form)
save(Object.assign(this.form, { table_name: this.tableName })).then(res => {
this.$Message.success({
content: `${this.type === 'add' ? '新增' : '编辑'}成功`
})
this.$emit("refresh")
this.hidden();
})
this.$refs["elForm"].validate((validate) => {
if (validate) {
this.formInfo.forEach((info) => {
if (info.edit_input === "files") {
this.form[info.field] = info._fileList.map(
(i) => i?.response?.id
);
}
});
console.log(this.form);
save(Object.assign(this.form, { table_name: this.tableName })).then(
(res) => {
this.$Message.success({
content: `${this.type === "add" ? "新增" : "编辑"}成功`,
});
this.$emit("refresh");
this.hidden();
}
);
}
});
}
},
},
computed: {},
watch: {
formInfo: {
handler: function (newVal) {
this.form = {};
this.rules = {}
newVal.forEach(i => {
if(i.field) {
this.rules = {};
newVal.forEach((i) => {
if (i.field) {
this.form[i.field] = "";
this.rules[i.field] = [{ required:true,message: `请填写${i.name}` }]
if(i.edit_input === 'checkbox' && i.parameter_id) {
getparameter({ id: i.parameter_id }).then(res => {
i.paramters = res.detail ?? []
})
if (i.validation instanceof Array && i.validation.length > 0) {
this.rules[i.field] = [
{ required: true, message: `请填写${i.name}` },
];
}
if (i.edit_input === "file" || i.edit_input === "files") {
this.form[i.field] = [];
}
if (i.edit_input === "checkbox") {
this.form[i.field] = [];
}
}
})
});
},
//immediate: true,
},
dialogVisible(val) {
if (val) {
if (this.type === "editor") {
this.$nextTick(() => this.getDetail())
this.$nextTick(() => this.getDetail());
}
} else {
this.id = "";
@ -237,8 +350,8 @@ export default {
this.$refs["elForm"].clearValidate();
delete this.form.id;
}
}
}
},
},
};
</script>

@ -0,0 +1,116 @@
<template>
<div>
<el-drawer
ref="elDrawer"
:title="detail.name || '详情'"
:visible.sync="isShow"
size="45%"
direction="rtl"
>
<template>
<el-descriptions
size="medium"
direction="vertical"
:column="3"
:labelStyle="{ 'font-weight': '600', width: '33%' }"
:content-style="{ width: '33%' }"
border
style="margin: 20px 10px"
>
<template v-for="info in formInfo">
<el-descriptions-item :label="info.name">{{
contentFormat(info)
}}</el-descriptions-item>
</template>
</el-descriptions>
</template>
</el-drawer>
</div>
</template>
<script>
import { show } from "@/api/system/baseForm";
export default {
props: {
formInfo: {
type: Array,
default: () => [],
},
tableName: String,
},
data() {
return {
id: "",
isShow: false,
detail: {},
};
},
methods: {
show() {
this.isShow = true;
},
hidden() {
this.isShow = false;
},
setId(id) {
if (typeof id == "number") {
this.id = id;
} else {
console.error("error typeof id: " + typeof id);
}
},
getId() {
return this.id;
},
},
computed: {
contentFormat() {
return function (i) {
if (i.parameter_id) {
return this.detail[i.link_with_name]?.value;
}
if (i.link_table_name) {
if (i.link_relation === "hasOne") {
return (
this.detail[i.link_with_name]?.name ||
this.detail[i.link_with_name]?.no ||
this.detail[i.link_with_name]?.value
);
}
if (i.link_relation === "hasMany") {
return this.detail[i.link_with_name]
?.map((o) => o?.name || o?.no || o?.value)
?.toString();
}
}
return this.detail[i.field]
};
},
},
watch: {
isShow(newVal) {
if (newVal) {
show(
{
table_name: this.tableName,
id: this.id,
},
true
).then((res) => {
console.log(111, res);
this.detail = res;
});
}
},
},
mounted() {},
};
</script>
<style scoped lang="scss">
.btns {
margin: 0 10px 12px 10px;
}
</style>

@ -1,248 +0,0 @@
<template>
<div>
<!-- 查询配置 -->
<div>
<div ref="lxHeader">
<LxHeader icon="md-apps" :text="customForm.tableName" style="margin-bottom: 10px; border: 0px; margin-top: 15px">
<div slot="content"></div>
<slot>
<header-content :auths="auths_auth_mixin">
<template #search>
<div style="display: flex">
<Select v-model="select.filter[0].key" style="width: 100px;" placeholder="搜索条目">
<Option v-for="item in form" :key="item.id" :value="item.field">{{ item.name }}</Option>
</Select>
<Select v-model="select.filter[0].op" style="width: 100px;margin-left: 10px;" placeholder="搜索条件">
<Option v-for="item in op" :key="item.value" :value="item.value">{{ item.label }}</Option>
</Select>
<template v-if="select.filter[0].op !== 'range' && !columnArrTest(select.filter[0].key)">
<Input v-model="select.filter[0].value" style="width: 150px;margin-left: 10px;" placeholder="请填写关键词"/>
</template>
<template v-else-if="select.filter[0].op !== 'range' && columnArrTest(select.filter[0].key)">
<Select v-model="select.filter[0].value" style="width: 150px;margin-left: 10px;" placeholder="请选择关键词">
<Option v-for="item in getColumnParams(select.filter[0].key)" :key="item.id" :value="item.id">{{ item.value }}</Option>
</Select>
</template>
<template v-else>
<Input :value="select.filter[0].value.split(',')[0]"
style="width: 150px;margin-left: 10px;"
placeholder="范围开始关键词"
@input="(e)=>inputStartHandler(e,select.filter[0])"/>
<span style="margin-left: 10px;display: flex;align-items: center;"></span>
<Input :value="select.filter[0].value.split(',')[1]"
style="width: 150px;margin-left: 10px;"
placeholder="范围结束关键词"
@input="(e)=>inputEndHandler(e,select.filter[0])"/>
</template>
<Button
style="margin-left: 10px"
type="primary"
@click="$refs['xyTable'].getTableData(true)"
>查询</Button
>
<xy-selectors
style="margin-left: 10px"
@reset="reset"
@search="$refs['xyTable'].getTableData(true)"
>
<template>
<div class="select">
<div class="select__item" v-for="(item,index) in select.filter">
<p>条件{{index+1}}</p>
<Select v-model="item.key" style="width: 100px;" placeholder="搜索条目">
<Option v-for="item in form" :key="item.id" :value="item.field">{{ item.name }}</Option>
</Select>
<Select v-model="item.op" style="width: 100px;margin-left: 10px;" placeholder="搜索条件">
<Option v-for="item in op" :key="item.value" :value="item.value">{{ item.label }}</Option>
</Select>
<template v-if="item.op !== 'range' && !columnArrTest(item.key)">
<Input v-model="item.value" style="width: 150px;margin-left: 10px;" placeholder="请填写关键词"/>
</template>
<template v-else-if="item.op !== 'range' && columnArrTest(item.key)">
<Select v-model="item.value" style="width: 150px;margin-left: 10px;" placeholder="请选择关键词">
<Option v-for="item in getColumnParams(item.key)" :key="item.id" :value="item.id">{{ item.value }}</Option>
</Select>
</template>
<template v-else>
<Input :value="item.value.split(',')[0]"
style="width: 150px;margin-left: 10px;"
placeholder="范围开始关键词"
@input="(e)=>inputStartHandler(e,item)"/>
<span style="margin-left: 10px;"></span>
<Input :value="item.value.split(',')[1]"
style="width: 150px;margin-left: 10px;"
placeholder="范围结束关键词"
@input="(e)=>inputEndHandler(e,item)"/>
</template>
<el-button v-if="index !== 0" size="small" type="danger" icon="el-icon-delete" circle style="margin-left: 10px;" @click="select.filter.splice(index,1)"></el-button>
</div>
</div>
<div class="add-btn">
<el-button size="small" type="primary" icon="el-icon-plus" circle @click="select.filter.push({key: '',op: '',value: ''})"></el-button>
<span>新增一条</span>
</div>
</template>
</xy-selectors>
</div>
</template>
<template #create>
<Button
type="primary"
@click="$refs['dialog'].setType('add'),$refs['dialog'].show()"
>新增</Button
>
</template>
</header-content>
</slot>
</LxHeader>
</div>
</div>
<xy-table
:auths="auths_auth_mixin"
:delay-req="true"
:destroy-action="destroy"
ref="xyTable"
:border="true"
:action="index"
:req-opt="Object.assign(select,selectForm)"
:destroy-req-opt="select"
:table-item="table"
@editor="row => {
$refs['dialog'].setId(row.id);
$refs['dialog'].setType('editor');
$refs['dialog'].show();
}"></xy-table>
<dialoger :table-name="customForm.tableName" :form-info="form" ref="dialog" @refresh="$refs['xyTable'].getTableData()"></dialoger>
</div>
</template>
<script>
import { index as fieldIndex } from "@/api/system/customFormField"
import { authMixin } from "@/mixin/authMixin";
import { index,destroy } from "@/api/system/baseForm"
import { op } from "@/const/op"
import dialoger from './dialog.vue'
import LxHeader from "@/components/LxHeader/index.vue";
import headerContent from "@/components/LxHeader/XyContent.vue";
export default {
components:{
LxHeader,
dialoger,
headerContent,
},
mixins: [authMixin],
data() {
return {
op,
select: {
table_name: "",
filter: [
{
key: "",
op: "",
value: ""
}
]
},
selectForm: [],
form: [],
table: [],
customForm: {
customFormId: "",
tableName: ""
}
}
},
methods: {
index,destroy,
reset() {
},
//target
inputStartHandler(e,target) {
let temp = target?.value.split(',')[1]
target.value = `${e},${temp ? temp : ""}`
},
inputEndHandler(e,target) {
let temp = target?.value.split(',')[0]
target.value = `${temp ? temp : ""},${e}`
},
async getField() {
if(this.$route.meta.params?.custom_form) {
let decode = decodeURIComponent(this.$route.meta.params?.custom_form)
try {
let custom_form = JSON.parse(decode)
this.customForm.customFormId = custom_form.custom_form_id
this.customForm.tableName = custom_form.table_name
this.select.table_name = custom_form.table_name
}catch (err) {
console.warn(err)
}
}
const res = await fieldIndex({
page: 1,
page_size: 999,
custom_form_id: this.customForm.customFormId
})
this.form = res.data
this.selectForm = ''
this.table = res.data.map(i => {
return {
prop: i.field,
label: i.name
}
})
}
},
computed: {
columnArrTest() {
return function (field) {
return this.form.find(i => i.field === field) ? this.form.find(i => i.field === field).search_input === 'checkbox' : false
}
},
getColumnParams() {
return function (field) {
return this.form.find(i => i.field === field) ? this.form.find(i => i.field === field).paramters : []
}
},
},
created() {
this.getField()
}
}
</script>
<style scoped lang="scss">
.select {
&__item {
& > p {
display: inline-block;
width: 80px;
text-align: center;
}
& + div {
margin-top: 6px;
}
}
}
.add-btn {
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
& > span {
padding: 0 10px;
}
}
</style>

@ -1,119 +1,328 @@
<template>
<div class="container">
<div>
<!-- 查询配置 -->
<div>
<div ref="lxHeader">
<LxHeader icon="md-apps" text="管理" style="margin-bottom: 10px; border: 0px; margin-top: 15px">
<LxHeader icon="md-apps" :text="customForm.tableName" style="margin-bottom: 10px; border: 0px; margin-top: 15px">
<div slot="content"></div>
<slot>
<div>
<Button type="primary" style="margin-left: 10px" @click="$refs['xyTable'].getTableData()"></Button>
<Button type="primary" style="margin-left: 10px">新增</Button>
</div>
<header-content :auths="auths_auth_mixin">
<template #search>
<div style="display: flex">
<Select v-model="select.filter[0].key" style="width: 100px;" placeholder="搜索条目">
<Option v-for="item in form" :key="item.id" :value="item.field">{{ item.name }}</Option>
</Select>
<Select v-model="select.filter[0].op" style="width: 100px;margin-left: 10px;" placeholder="搜索条件">
<Option v-for="item in op" :key="item.value" :value="item.value">{{ item.label }}</Option>
</Select>
<template v-if="select.filter[0].op !== 'range' && !columnArrTest(select.filter[0].key)">
<Input v-model="select.filter[0].value" style="width: 150px;margin-left: 10px;" placeholder="请填写关键词"/>
</template>
<template v-else-if="select.filter[0].op !== 'range' && columnArrTest(select.filter[0].key)">
<Select v-model="select.filter[0].value" style="width: 150px;margin-left: 10px;" placeholder="请选择关键词">
<Option v-for="item in getColumnParams(select.filter[0].key)" :key="item.id" :value="item.id">{{ item.value || item.name || item.no || item.id }}</Option>
</Select>
</template>
<template v-else>
<Input :value="select.filter[0].value.split(',')[0]"
style="width: 150px;margin-left: 10px;"
placeholder="范围开始关键词"
@input="(e)=>inputStartHandler(e,select.filter[0])"/>
<span style="margin-left: 10px;display: flex;align-items: center;"></span>
<Input :value="select.filter[0].value.split(',')[1]"
style="width: 150px;margin-left: 10px;"
placeholder="范围结束关键词"
@input="(e)=>inputEndHandler(e,select.filter[0])"/>
</template>
<Button
style="margin-left: 10px"
type="primary"
@click="$refs['xyTable'].getTableData(true)"
>查询</Button
>
<xy-selectors
style="margin-left: 10px"
@reset="reset"
@search="$refs['xyTable'].getTableData(true)"
>
<template>
<div class="select">
<div class="select__item" v-for="(item,index) in select.filter" :key="`${item.value}-${index}`">
<p>条件{{index+1}}</p>
<Select v-model="item.key" style="width: 100px;" placeholder="搜索条目">
<Option v-for="item in form" :key="item.id" :value="item.field">{{ item.name }}</Option>
</Select>
<Select v-model="item.op" style="width: 100px;margin-left: 10px;" placeholder="搜索条件">
<Option v-for="item in op" :key="item.value" :value="item.value">{{ item.label }}</Option>
</Select>
<template v-if="item.op !== 'range' && !columnArrTest(item.key)">
<Input v-model="item.value" style="width: 150px;margin-left: 10px;" placeholder="请填写关键词"/>
</template>
<template v-else-if="item.op !== 'range' && columnArrTest(item.key)">
<Select v-model="item.value" style="width: 150px;margin-left: 10px;" placeholder="请选择关键词">
<Option v-for="item in getColumnParams(item.key)" :key="item.id" :value="item.id">{{ item.value || item.name || item.no || item.id }}</Option>
</Select>
</template>
<template v-else>
<Input :value="item.value.split(',')[0]"
style="width: 150px;margin-left: 10px;"
placeholder="范围开始关键词"
@input="(e)=>inputStartHandler(e,item)"/>
<span style="margin-left: 10px;"></span>
<Input :value="item.value.split(',')[1]"
style="width: 150px;margin-left: 10px;"
placeholder="范围结束关键词"
@input="(e)=>inputEndHandler(e,item)"/>
</template>
<el-button v-if="index !== 0" size="small" type="danger" icon="el-icon-delete" circle style="margin-left: 10px;" @click="select.filter.splice(index,1)"></el-button>
</div>
</div>
<div class="add-btn">
<el-button size="small" type="primary" icon="el-icon-plus" circle @click="select.filter.push({key: '',op: '',value: ''})"></el-button>
<span>新增一条</span>
</div>
</template>
</xy-selectors>
</div>
</template>
<template #create>
<Button
type="primary"
@click="$refs['dialog'].setType('add'),$refs['dialog'].show()"
>新增</Button
>
</template>
<template #export>
<Button
type="primary"
@click="download('/api/admin/base-form/index','get',Object.assign(select,{ page: 1,page_size: 9999,is_export: 1 }),`${new Date().getTime()}.xlsx`)"
>导出</Button
>
</template>
</header-content>
</slot>
</LxHeader>
</div>
</div>
<xy-table
:auths="auths_auth_mixin"
:delay-req="true"
:destroy-action="destroy"
ref="xyTable"
:border="true"
:list="[]"
:req-opt="{keyword:'123'}"
res-prop=""
:table-item="table"></xy-table>
:action="index"
:req-opt="Object.assign(select,selectForm)"
:destroy-req-opt="select"
:table-item="table"
@detail="row => {
$refs['drawer'].setId(row.id);
$refs['drawer'].show();
}"
@editor="row => {
$refs['dialog'].setId(row.id);
$refs['dialog'].setType('editor');
$refs['dialog'].show();
}">
</xy-table>
<dialoger :table-name="customForm.tableName" :form-info="form" ref="dialog" @refresh="$refs['xyTable'].getTableData()"></dialoger>
<drawer :table-name="customForm.tableName" :form-info="form" ref="drawer"></drawer>
</div>
</template>
<script>
import { index as fieldIndex } from "@/api/system/customFormField"
import { authMixin } from "@/mixin/authMixin";
import { index,destroy } from "@/api/system/baseForm";
import { op } from "@/const/op";
import { download } from "@/utils/downloadRequest";
import { getparameter } from "@/api/system/dictionary";
import dialoger from './dialog.vue';
import LxHeader from "@/components/LxHeader/index.vue";
import { getData } from './data'
import {listmenu} from '@/api/system/menu'
import headerContent from "@/components/LxHeader/XyContent.vue";
import drawer from "@/views/component/drawer.vue";
export default {
components:{
LxHeader
LxHeader,
dialoger,
headerContent,
drawer,
},
mixins: [authMixin],
data() {
return {
select:{
page:1,
page_size:10
op,
select: {
table_name: "",
filter: [
{
key: "",
op: "",
value: ""
}
]
},
list:[],
table:[
{
type:'expand',
width:46,
expandFn:props => {
selectForm: [],
form: [],
table: [],
customForm: {
customFormId: "",
tableName: ""
}
}
},
methods: {
index,destroy,download,
reset() {
this.select.filter.splice(1)
this.select.filter[0] = {
key: "",
op: "",
value: ""
}
},
//target
inputStartHandler(e,target) {
let temp = target?.value.split(',')[1]
target.value = `${e},${temp ? temp : ""}`
},
inputEndHandler(e,target) {
let temp = target?.value.split(',')[0]
target.value = `${temp ? temp : ""},${e}`
},
async getField() {
if(this.$route.meta.params?.custom_form) {
let decode = decodeURIComponent(this.$route.meta.params?.custom_form)
try {
let custom_form = JSON.parse(decode)
this.customForm.customFormId = custom_form.custom_form_id
this.customForm.tableName = custom_form.table_name
this.select.table_name = custom_form.table_name
}catch (err) {
console.warn(err)
}
}
const res = await fieldIndex({
page: 1,
page_size: 999,
custom_form_id: this.customForm.customFormId,
sort_name: 'sort',
sort_type: 'asc',
})
if(res.data && res.data instanceof Array) {
res.data.forEach(i => {
if (i.field) {
if (i.edit_input === "file" || i.edit_input === "files") {
i._fileList = [];
}
if (
(i.edit_input === "checkbox" || i.edit_input === "radio") &&
i.parameter_id
) {
getparameter({ id: i.parameter_id }).then((res) => {
i._paramters = res.detail ?? [];
});
}
if (
(i.edit_input === "checkbox" || i.edit_input === "radio") &&
i.link_table_name
) {
index({
page: 1,
page_size: 999,
table_name: i.link_table_name,
}).then((res) => {
i._paramters = res.data ?? [];
});
}
}
})
}
this.form = res.data
this.selectForm = ''
this.table = res.data.map(i => {
let linkOb = {}
if(i.parameter_id) {
linkOb.customFn = row => {
return (
<div>{props.$index}</div>
<span>{ row[i.link_with_name]?.value }</span>
)
}
},
{
prop:'id',
label:'编号',
width:80,
sortable: 'custom'
},
{
prop:'name',
label:'菜单',
width:120,
sortable:'custom',
},
{
prop:'url',
label:'菜单路径',
minWidth:140
},
{
prop:'path',
label:'路由',
minWidth:140
},
{
prop:'icon',
label:'图标',
minWidth:100
},
{
prop:'api_prefix',
label:'API前缀',
minWidth:100
},
{
prop:'sortnumber',
label:'排序',
width:100,
filters:[
{
text:"0",
value:0
},
{
text:"1",
value:1
}
if(i.link_table_name) {
if(i.link_relation === 'hasOne') {
linkOb.customFn = row => {
return (
<span>{ row[i.link_with_name]?.name || row[i.link_with_name]?.no || row[i.link_with_name]?.value }</span>
)
}
]
},
]
}
if(i.link_relation === 'hasMany') {
linkOb.customFn = row => {
return (
<div>
{ row[i.link_with_name]?.map(o => (<span>{ o?.name || o?.no || o?.value }</span>)) }
</div>
)
}
}
}
return Object.assign({
prop: i.field,
label: i.name
},linkOb)
})
}
},
methods: {
listmenu,
getList(){
this.total = getData(this.select).total
this.list = getData(this.select).data
}
computed: {
columnArrTest() {
return function (field) {
return this.form.find(i => i.field === field) ? this.form.find(i => i.field === field).search_input === 'checkbox' : false
}
},
getColumnParams() {
return function (field) {
return this.form.find(i => i.field === field) ? this.form.find(i => i.field === field)._paramters : []
}
},
},
computed: {},
mounted() {
this.getList()
created() {
this.getField()
}
}
</script>
<style scoped lang="scss">
.select {
&__item {
& > p {
display: inline-block;
width: 80px;
text-align: center;
}
& + div {
margin-top: 6px;
}
}
}
.add-btn {
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
& > span {
padding: 0 10px;
}
}
</style>

@ -1,305 +0,0 @@
<template>
<div>
<xy-dialog
ref="dialog"
:z-index="zIndex"
:is-show.sync="isShow"
type="form"
:title="type === 'add' ? '新增字段' : '编辑字段'"
:form="form"
:rules="rules"
@submit="submit"
>
<template v-slot:field>
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red; font-weight: 600; padding-right: 4px"
>*</span
>
字段名
</div>
<div class="xy-table-item-content">
<el-input
v-model="form.field"
clearable
placeholder="请输入字段名"
style="width: 300px"
></el-input>
</div>
</div>
</template>
<template v-slot:name>
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red; font-weight: 600; padding-right: 4px"
>*</span
>
名字
</div>
<div class="xy-table-item-content">
<el-input
v-model="form.name"
clearable
placeholder="请输入名字"
style="width: 300px"
></el-input>
</div>
</div>
</template>
<template v-slot:parameter_id>
<div class="xy-table-item">
<div class="xy-table-item-label">数据字典 </div>
<div class="xy-table-item-content">
<el-select
v-model="form.parameter_id"
clearable
:popper-append-to-body="false"
placeholder="请选择数据字典"
style="width: 300px"
>
<el-option
v-for="item in parameters"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</div>
</div>
</template>
<template v-slot:search_input>
<div class="xy-table-item">
<div class="xy-table-item-label">查询输入类型 </div>
<div class="xy-table-item-content">
<el-select
filterable
:popper-append-to-body="false"
v-model="form.search_input"
clearable
placeholder="请选择查询输入类型"
style="width: 300px"
>
<el-option v-for="item in config" :key="item.id" :value="item.edit_input" :label="item.name">
</el-option>
<!-- <el-option-group-->
<!-- v-for="group in inputTypes"-->
<!-- :key="group.label"-->
<!-- :label="group.label">-->
<!-- <el-option-->
<!-- v-for="item in group.options"-->
<!-- :key="item.value"-->
<!-- :label="item.label"-->
<!-- :value="item.value">-->
<!-- </el-option>-->
<!-- </el-option-group>-->
</el-select>
</div>
</div>
</template>
<template v-slot:edit_input>
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red; font-weight: 600; padding-right: 4px"
>*</span
>
编辑输入类型
</div>
<div class="xy-table-item-content">
<el-select
filterable
:popper-append-to-body="false"
v-model="form.edit_input"
clearable
placeholder="请选择编辑输入类型"
style="width: 300px"
>
<el-option v-for="item in config" :key="item.id" :value="item.edit_input" :label="item.name">
</el-option>
<!-- <el-option-group-->
<!-- v-for="group in inputTypes"-->
<!-- :key="group.label"-->
<!-- :label="group.label">-->
<!-- <el-option-->
<!-- v-for="item in group.options"-->
<!-- :key="item.value"-->
<!-- :label="item.label"-->
<!-- :value="item.value">-->
<!-- </el-option>-->
<!-- </el-option-group>-->
</el-select>
</div>
</div>
</template>
</xy-dialog>
</div>
</template>
<script>
import { config } from "@/api/system/customForm";
import { listparameter } from "@/api/system/dictionary";
import { show, save } from "@/api/system/customFormField";
import inputTypes from "@/const/inputType"
export default {
props: {
zIndex:Number,
},
data() {
return {
parameters: [],
config: [],
//inputTypes,
isShow: false,
id: "",
type: "",
form: {
custom_form_id: "",
field: "",
name: "",
parameter_id: "",
search_input: "",
edit_input: "",
},
rules: {
field: [
{
required: true,
message: "请填写字段名",
},
{
pattern: /^[a-z_]+$/,
message: '字段名只能包含小写英文字母和下划线'
},
],
name: [
{
required: true,
message: "请填写名字",
},
],
edit_input: [
{
required: true,
message: "请填写编辑输入类型",
},
],
},
};
},
methods: {
show() {
this.isShow = true;
},
hidden() {
this.isShow = false;
},
init() {
this.form = {
field: "",
name: "",
parameter_id: "",
search_input: "",
edit_input: "",
};
},
setId(id) {
if (typeof id == "number") {
this.id = id;
} else {
console.error("error typeof id: " + typeof id);
}
},
getId() {
return this.id;
},
setType(type = "add") {
let types = ["add", "editor"];
if (types.includes(type)) {
this.type = type;
} else {
console.warn("Unknown type: " + type);
}
},
setForm(key = [], value = []) {
if (key instanceof Array) {
key.forEach((key, index) => {
this.form[key] = value[index] ?? "";
});
}
if (typeof key === "string") {
this.form[key] = value;
}
if (!key) {
this.init();
}
},
async getParameters() {
const res = await listparameter({ page: 1,page_size :999} )
this.parameters = res.data
},
async getConfig() {
let res = await config()
this.config = res.edit_to_migration
console.log(this.config)
},
async getDetail() {
const res = await show({ id: this.id });
this.$integrateData(this.form, res);
},
submit() {
if (this.type === "add") {
if (this.form.hasOwnProperty("id")) {
delete this.form.id;
}
}
if (this.type === "editor") {
Object.defineProperty(this.form, "id", {
value: this.id,
enumerable: true,
configurable: true,
writable: true,
});
}
save(this.form).then((res) => {
this.$message({
type: "success",
message: this.type === "add" ? "新增字段" : "编辑字段" + "成功",
});
this.isShow = false;
this.$emit("refresh");
});
},
},
watch: {
isShow(val) {
if (val) {
if (this.type === "editor") {
this.getDetail();
}
} else {
this.id = "";
this.type = "";
this.init();
this.$refs["dialog"].clearValidate();
delete this.form.id;
}
},
},
created() {
this.getConfig();
this.getParameters();
}
};
</script>
<style scoped lang="scss">
::v-deep .el-input__inner {
text-align: left;
}
.xy-table-item-label {
width: 180px;
}
</style>

@ -0,0 +1,285 @@
<template>
<div style="padding: 0 10px; height: 100%; overflow: scroll">
<template v-if="selectedForm">
<el-form
:rules="formRule"
ref="form"
:model="selectedForm"
label-width="80px"
>
<el-form-item prop="name" label="字段名称">
<el-input
v-model="selectedForm.name"
placeholder="请输入字段名称"
@input="inputHandler"
></el-input>
</el-form-item>
<el-form-item required prop="field" label="字段标识">
<el-input v-model="selectedForm.field" disabled></el-input>
</el-form-item>
<el-form-item label="关联方式">
<el-select
v-model="linkType"
clearable
:popper-append-to-body="false"
placeholder="请选择关联方式"
style="width: 100%"
>
<el-option
v-for="(item, index) in ['关联表', '数据字典']"
:key="index"
:label="item"
:value="item"
></el-option>
</el-select>
</el-form-item>
<el-form-item
prop="parameter_id"
label="数据字典"
v-show="linkType === '数据字典'"
>
<el-select
v-model="selectedForm.parameter_id"
clearable
:popper-append-to-body="false"
placeholder="请选择数据字典"
style="width: 100%"
>
<el-option
v-for="item in parameters"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item
prop="link_table_name"
label="关联表"
v-show="linkType === '关联表'"
>
<el-select
v-model="selectedForm.link_table_name"
clearable
:popper-append-to-body="false"
placeholder="请选择关联表"
style="width: 100%"
>
<el-option
v-for="item in forms"
:key="item.id"
:label="item.name"
:value="item.table_name"
></el-option>
</el-select>
</el-form-item>
<el-form-item
prop="link_relation"
label="关联类型"
v-show="linkType === '关联表'"
>
<el-select
v-model="selectedForm.link_relation"
:popper-append-to-body="false"
placeholder="请选择关联类型"
style="width: 100%"
>
<el-option
v-for="item in [
{ value: 'hasOne', label: '一对一' },
{ value: 'hasMany', label: '一对多' },
]"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item prop="help" label="帮助文字">
<el-input
v-model="selectedForm.help"
placeholder="请输入帮助文字"
></el-input>
</el-form-item>
<el-form-item prop="validation" label="校验规则">
<el-select
v-model="selectedForm.validation"
clearable
multiple
collapse-tags
:popper-append-to-body="false"
placeholder="请选择校验规则"
style="width: 100%"
>
<el-option
v-for="(value, key) in rules"
:key="key"
:label="value"
:value="key"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="是否显示" prop="list_show">
<el-switch
v-model="selectedForm.list_show"
:active-value="1"
:inactive-value="0"
></el-switch>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveField"></el-button>
<el-button
@click="
(linkType = ''),
$store.commit('form/CLEAR_SELECTED'),
$store.commit('form/CLEAR_SELECTED_INDEX')
"
>清除</el-button
>
<el-popover
trigger="hover"
ref="el-popover"
placement="top"
width="160"
>
<p>确定删除字段吗</p>
<div style="text-align: right; margin: 0">
<el-button
size="mini"
type="text"
@click="$refs['el-popover'].doClose()"
>取消</el-button
>
<el-button
type="primary"
size="mini"
@click="
$store.commit('form/SPLICE_FORM_LIST', {
index: selectedIndex,
length: 1,
}),
$store.commit('form/CLEAR_SELECTED_INDEX'),
$store.commit('form/CLEAR_SELECTED'),
$refs['el-popover'].doClose()
"
>确定</el-button
>
</div>
<el-button
type="danger"
slot="reference"
@click="$refs['el-popover'].doShow()"
style="margin-left: 10px"
>删除</el-button
>
</el-popover>
</el-form-item>
</el-form>
</template>
<template v-else>
<el-empty
style="position: relative; top: 50%; transform: translateY(-50%)"
description="暂无选择"
></el-empty>
</template>
</div>
</template>
<script>
import { index as formIndex } from "@/api/system/customForm";
import { listparameter } from "@/api/system/dictionary";
import { translate } from "@/api/system/customFormField";
import { debounce } from "@/utils";
import { mapState } from "vuex";
export default {
props: {
rules: Object,
types: Array,
},
data() {
return {
linkType: "",
debouncedInputHandler: null,
formRule: {
name: [
{ required: true, message: "请输入字段名称" },
{
validator: this.checkChinese,
},
],
},
parameters: [], //
forms: [], //
};
},
methods: {
checkChinese(rule, value, callback) {
const reg = /^[\u4e00-\u9fa5]+$/;
if (reg.test(value)) {
callback();
} else {
callback(new Error("请输入中文姓名"));
}
},
inputHandler(e) {
this.debouncedInputHandler(e);
},
async getForms() {
const res = await formIndex({ page: 1, page_size: 999 });
this.forms = res.data;
},
async getParameters() {
const res = await listparameter({ page: 1, page_size: 999 });
this.parameters = res.data;
},
saveField() {
this.$store.commit("form/SPLICE_FORM_LIST", {
index: this.selectedIndex,
length: 1,
config: this.selectedForm,
});
this.$store.commit("form/CLEAR_SELECTED");
this.$store.commit("form/CLEAR_SELECTED_INDEX");
},
},
computed: {
...mapState("form", ["selectedForm", "formList", "selectedIndex"]),
},
watch: {
selectedForm(newVal) {
if (newVal?.parameter_id) {
this.linkType = "数据字典";
} else if (newVal?.link_table_name) {
this.linkType = "关联表";
} else {
this.linkType = "";
}
},
},
created() {
this.getForms();
this.getParameters();
this.debouncedInputHandler = debounce((e) => {
if (e) {
this.checkChinese("", e, (err) => {
if (!err) {
translate({
str: e,
}).then((en) => {
this.selectedForm.field = en.pinyin;
});
}
});
}
}, 1000);
},
};
</script>
<style scoped lang="scss"></style>

@ -0,0 +1,350 @@
<template>
<div>
<el-dialog
title="提示"
:visible.sync="dialogVisible"
:fullscreen="true"
:before-close="handleClose"
>
<template #title>
<div class="btns">
<span class="title">{{ custom_form.name }}</span>
<el-button
size="small"
type="primary"
icon="el-icon-refresh-right"
circle
@click="$store.dispatch('form/getFormList')"
></el-button>
<el-button
size="small"
type="primary"
icon="el-icon-refresh"
circle
@click="update({ id:custom_form_id })"
></el-button>
<el-button
size="small"
type="primary"
icon="el-icon-check"
circle
@click="submit"
></el-button>
</div>
</template>
<template>
<div style="height: 100%">
<Split v-model="splitL" :min="0.05">
<template #left>
<draggable
v-model="types"
:sort="false"
:group="{ name: 'items', pull: 'clone' }"
>
<div class="type-item" v-for="item in types">
{{ item.name }}
</div>
</draggable>
</template>
<template #right>
<Split v-model="splitR" :min="0.2">
<template #left>
<div style="height: 100%">
<el-dialog
id="template-dialog"
:title="custom_form.name || '模板表单'"
style="position: relative; inset: 0 0 0 0; height: 100%"
width="600px"
top="40px"
:visible="true"
:modal="false"
:append-to-body="false"
:modal-append-to-body="false"
>
<template>
<el-form
label-width="80px"
label-position="right"
size="small"
>
<draggable
:value="formList"
group="items"
@change="changeHandler"
>
<el-form-item
:label="i.name || '字段名称'"
:required="
!!i.validation && i.validation.length > 0
"
v-for="(i, index) in formList"
@click.native="selectPick(i, index)"
>
<formSlotRender
:config="i"
:index="index"
></formSlotRender>
</el-form-item>
</draggable>
</el-form>
</template>
<div slot="footer">
<div>
<el-button> </el-button>
<el-button type="warning" plain> </el-button>
<el-button type="primary"> </el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<template #right>
<editPane :rules="rules" :types="types"></editPane>
</template>
</Split>
</template>
</Split>
</div>
</template>
</el-dialog>
</div>
</template>
<script>
import { listparameter } from "@/api/system/dictionary";
import { index, config } from "@/api/system/customFormField";
import { mapState } from "vuex";
import { index as formIndex } from "@/api/system/customForm";
import { deepCopy } from "@/utils";
import { update } from "@/api/system/customForm";
import formSlotRender from "../components/formSlotRender.vue";
import editPane from "../components/editPane.vue";
export default {
components: {
formSlotRender,
editPane,
},
data() {
return {
//
splitL: 0.13,
splitR: 0.7,
custom_form: {},
custom_form_id: "",
dialogVisible: false,
types: [], //
rules: [],
};
},
methods: {
deepCopy,update,
show() {
this.dialogVisible = true;
},
hidden() {
this.dialogVisible = false;
},
/**
* @param key:string | array
* @param value:string | array
**/
set(key, value) {
if (
key instanceof Array &&
value instanceof Array &&
key?.length === value?.length
) {
key.forEach((k, i) => {
this[k] = value[i];
});
} else if (typeof key === "string") {
this[key] = value;
} else {
throw new Error("参数key类型错误");
}
},
handleClose(done) {
this.$confirm("确认退出?")
.then((_) => {
done();
})
.catch((_) => {});
},
//
async getForms() {
const res = await formIndex({ page: 1, page_size: 999 });
this.forms = res.data;
},
async getParameters() {
const res = await listparameter({ page: 1, page_size: 999 });
this.parameters = res.data;
},
async getConfig() {
const { edit_to_migration, validation_rules } = await config(false);
this.types = edit_to_migration;
this.rules = validation_rules;
},
selectPick(i, index) {
this.$store.commit("form/SET_SELECTED_INDEX", index);
this.$store.commit("form/SET_SELECTED", deepCopy(i));
},
//
changeHandler(e) {
if (e.added) {
const { element, newIndex } = e.added;
const originalData = element;
const newItem = {
id: "",
custom_form_id: this.custom_form_id,
field: "",
name: "",
parameter_id: "",
search_input: originalData.edit_input,
edit_input: originalData.edit_input,
sort: newIndex,
help: "",
validation: "",
select_item: "",
list_show: 1,
link_table_name: "",
link_relation: "",
};
console.log(newItem);
this.$store.commit("form/SPLICE_FORM_LIST", {
index: newIndex,
length: 0,
config: newItem,
});
}
if (e.moved) {
const { element, newIndex, oldIndex } = e.moved;
if (element?.sort) {
element.sort = newIndex;
}
this.$store.commit("form/SPLICE_FORM_LIST", {
index: oldIndex,
length: 1,
});
this.$store.commit("form/SPLICE_FORM_LIST", {
index: newIndex,
length: 0,
config: element,
});
}
},
submit() {
this.$store.dispatch("form/submit").then((res) => {
setTimeout(() => {
update({ id: this.custom_form_id })
.then((res) => {
this.$message({
type: "success",
message: "数据库同步成功",
});
})
.catch((err) => {
this.$message({
type: "error",
message: "数据库同步失败,请手动同步",
});
});
}, 2000);
});
},
},
computed: {
...mapState("form", ["selectedForm", "formList", "selectedIndex"]),
},
watch: {
async dialogVisible(newVal) {
if (newVal) {
await this.$store.dispatch("form/getFormList", this.custom_form_id);
}
},
},
created() {
this.getConfig();
},
};
</script>
<style scoped lang="scss">
.title {
font-weight: 600;
letter-spacing: 2px;
padding: 0 20px;
}
.type-item {
min-width: 40px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
color: $primaryColor;
border: solid 1px $primaryColor;
border-radius: 0.4em;
position: relative;
transition: all 0.2s ease-out;
padding: 0.5em 1em;
margin: 0.6em 1em;
&:hover {
color: #fff;
background: $primaryColor;
}
}
::v-deep .el-dialog__headerbtn {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
::v-deep .el-dialog__header {
padding: 10px !important;
box-shadow: 0 2px 10px 2px rgba(0, 0, 0, 0.2);
position: relative;
}
::v-deep .el-dialog__body {
height: calc(100% - 62px);
padding: 8px 0 !important;
}
#template-dialog {
::v-deep .el-dialog__header {
box-shadow: none;
padding: 20px;
position: relative;
}
::v-deep .el-dialog__body {
padding: 30px 20px !important;
}
}
::v-deep .ivu-split-trigger-vertical {
width: 2px;
}
::v-deep .ivu-split-trigger-bar-con.vertical {
display: none;
}
::v-deep .ivu-split-horizontal > .ivu-split-trigger-con {
width: 2px;
}
::v-deep .ivu-split-pane {
height: 100%;
overflow: scroll;
&::-webkit-scrollbar {
width: 2px;
}
}
</style>

@ -1,159 +0,0 @@
<template>
<div>
<el-drawer
ref="elDrawer"
title="自定义字段"
:visible.sync="isShow"
size="49%"
direction="rtl"
>
<template>
<div class="btns">
<el-button
size="small"
type="primary"
icon="el-icon-refresh"
circle
@click="$refs['xyTable'].getTableData(true)"
></el-button>
<el-button
size="small"
type="primary"
icon="el-icon-plus"
circle
@click="$refs['addField'].setForm(['custom_form_id'],[id]),$refs['addField'].setType('add'), $refs['addField'].show()"
></el-button>
<el-button
size="small"
type="primary"
icon="el-icon-search"
circle
></el-button>
</div>
<xy-table
:delay-req="true"
:auths="['delete','edit']"
:action="index"
:destroy-action="destroy"
:req-opt="{
custom_form_id: id
}"
ref="xyTable"
:height="400"
style="margin: 0 10px"
:table-item="table"
@editor="row => {
$refs['addField'].setType('editor');
$refs['addField'].setId(row.id);
$refs['addField'].show();
}"
@destroyed="row => {
update({ id })
}"
>
</xy-table>
</template>
</el-drawer>
<addField
:z-index="zIndex"
ref="addField"
@refresh="refreshCallback"
></addField>
</div>
</template>
<script>
import { update } from "@/api/system/customForm"
import { index,destroy } from "@/api/system/customFormField";
import addField from "./addField.vue";
export default {
components: {
addField,
},
data() {
return {
zIndex: 2001,
select: {
custom_form_id: this.id
},
id: null,
isShow: false,
table: [
{
type: "index",
width: 70,
label: "编号",
},
{
prop: "field",
label: "字段标识",
width: 120,
},
{
prop: "name",
label: "名字",
width: 160,
},
],
};
},
methods: {
index,destroy,update,
show() {
this.isShow = true;
},
hidden() {
this.isShow = false;
},
setId(id) {
if (typeof id == "number") {
this.id = id;
} else {
console.error("error typeof id: " + typeof id);
}
},
getId() {
return this.id;
},
refreshCallback() {
this.$refs['xyTable'].getTableData()
update({ id:this.id })
}
},
computed: {},
watch: {
isShow: {
handler: function (newVal) {
if(newVal) {
this.zIndex = Number(
window
.getComputedStyle(this.$refs["elDrawer"].$el)
.getPropertyValue("z-index")
)
? Number(
window
.getComputedStyle(this.$refs["elDrawer"].$el)
.getPropertyValue("z-index")
) + 1
: 2100;
this.$nextTick(() => {
this.$refs['xyTable'].getTableData(true);
})
}
}
}
},
mounted() {
},
};
</script>
<style scoped lang="scss">
.btns {
margin: 0 10px 12px 10px;
}
</style>

@ -0,0 +1,87 @@
<script>
import { domMap } from "@/const/inputType";
import { templatePropsMap } from "@/const/templateProps";
export default {
props: {
config: Object,
index: Number,
},
data() {
return {
};
},
methods: {
optionsRender(h) {
if (this.config.edit_input === "radio") {
return ['选项1','选项2','选项3'].map(i => h('el-radio',{ label: i }, i))
}
if(this.config.edit_input === "checkbox") {
return ['选项1','选项2','选项3'].map(i => h('el-checkbox',{ label: i }, i))
}
if(this.config.edit_input === "file" || this.config.edit_input === "files") {
return [
h('el-button',{
slot: 'trigger',
props: {
size: 'small',
type: 'primary'
}
}, '选取文件'),
h('el-button',{
style: {
'margin-left': '10px'
},
props: {
size: 'small',
type: 'success'
}
}, '上传到服务器'),
h('div',{
class: 'el-upload__tip',
slot: 'tip'
},'文件不超过500kb')
]
}
},
},
computed: {},
render(h) {
return h(
"div",
{
class: this.config.list_show ? "" : "no-list-show",
style: {
opacity: this.config.list_show ? 1 : 0.5,
filter:
this.index === this.$store.state.form.selectedIndex
? "drop-shadow(0 0 2px #0077CCFF) drop-shadow(0 0 8px #449FD9FF)"
: "",
position: "relative",
},
},
[
h(domMap.get(this.config.edit_input), {
style: {
width: "100%",
},
props: templatePropsMap.get(this.config.edit_input),
},this.optionsRender(h)),
]
);
},
};
</script>
<style scoped lang="scss">
.no-list-show {
&::after {
content: "隐藏";
color: red;
font-size: 12px;
position: absolute;
right: 10px;
top: 0;
}
}
</style>

@ -30,13 +30,13 @@
<template #setting="scope">
<Button type="primary"
size="small"
@click="$refs['formFields'].setId(scope.row.id),$refs['formFields'].show()">字段</Button>
@click="$refs['formEditor'].set(['custom_form_id','custom_form'],[scope.row.id,scope.row]),$refs['formEditor'].show()">字段</Button>
</template>
</xy-table>
<addForm ref="addForm" @refresh="$refs['xyTable'].getTableData()"></addForm>
<formFields ref="formFields"></formFields>
<formEditor ref="formEditor"></formEditor>
</div>
</template>
@ -46,10 +46,10 @@ import { authMixin } from "@/mixin/authMixin";
import LxHeader from "@/components/LxHeader/index.vue";
import addForm from "./components/addForm.vue";
import formFields from "./components/formFields.vue";
import formEditor from "@/views/system/components/formEditor.vue";
export default {
components:{
LxHeader,addForm,formFields
LxHeader,addForm,formEditor
},
mixins: [authMixin],

@ -124,7 +124,7 @@
<el-row>
<el-col :span="18">
<el-form-item label="表单">
<el-select value-key="id" v-model="selectCustomForm">
<el-select clearable value-key="id" v-model="selectCustomForm">
<el-option
v-for="item in customForms"
:key="item.id"
@ -395,7 +395,7 @@ export default {
var result = Object.assign(this.form, obj);
this.form = result;
if(this.form.path.includes('?')) {
if(this.form.path?.includes('?')) {
let queryString = this.form.path.split("?")[1];
// &
let paramsArray = queryString.split("&");

Loading…
Cancel
Save