init commit & 混料程序模块
This commit is contained in:
0
src/components/.gitkeep
Normal file
0
src/components/.gitkeep
Normal file
301
src/components/AttrForm.vue
Normal file
301
src/components/AttrForm.vue
Normal file
@@ -0,0 +1,301 @@
|
||||
<template>
|
||||
<div class="attr-form">
|
||||
<h3>
|
||||
{{ title }} <el-button style="margin-left: 8px;" type="text" v-if="!isDetail && !showAddAttr" @click="showAddAttr = true">{{ $t('add') }}</el-button>
|
||||
</h3>
|
||||
<div v-if="!showAddAttr">
|
||||
<component
|
||||
key="sub-table"
|
||||
:is="require('./BaseTable.vue').default"
|
||||
:table-head-configs="filterTableConfigs()"
|
||||
:data="dataList"
|
||||
:page="pageIndex"
|
||||
:size="pageSize"
|
||||
:max-height="calcMaxHeight(8)"
|
||||
@operate-event="handleOperations"
|
||||
/>
|
||||
<el-pagination
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"
|
||||
:current-page="pageIndex"
|
||||
:page-sizes="[5, 10, 20, 50]"
|
||||
:page-size="pageSize"
|
||||
:total="totalPage"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
<div v-else style="background: #eee; border-radius: 8px; padding: 12px;">
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form ref="AttrForm" :model="AttrForm" :rules="AttrFormRules" :inline="true" label-position="top">
|
||||
<el-row :gutter="20" style="padding: 0 24px;">
|
||||
<el-col :span="attrFormFields.length > 6 ? 6 : 12" v-for="field in attrFormFields" :key="field.prop + 'col'">
|
||||
<el-form-item :key="field.prop" :prop="field.prop" :label="field.name" style="width: 100%">
|
||||
<el-input v-if="field.formType === 'input' || !field.formType" v-model="AttrForm[field.prop]" :placeholder="$t('hints.input')" clearable />
|
||||
<el-select v-if="field.formType === 'select'" v-model="AttrForm[field.prop]" clearable>
|
||||
<el-option v-for="opt in field.formOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
<!-- add more... -->
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="text-align: right;">
|
||||
<el-button size="small" @click="handleCloseAttrForm">{{ $t('cancel') }}</el-button>
|
||||
<el-button type="success" size="small" @click="handleSaveAttrForm">{{ $t('save') }}</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import i18n from '@/i18n'
|
||||
import BaseTable from '@/components/base-table'
|
||||
import { pick } from 'lodash/object'
|
||||
|
||||
|
||||
/** 计算表格的最大高 */
|
||||
function calcMaxHeight(num) {
|
||||
const FIXED_HEIGHT = 220
|
||||
let clientHeight = 0
|
||||
const bodyHeight = document.body.clientHeight || null
|
||||
const documentHeight = document.documentElement.clientHeight || null
|
||||
if (bodyHeight && documentHeight) {
|
||||
clientHeight = Math.max(bodyHeight, documentHeight)
|
||||
} else {
|
||||
clientHeight = documentHeight ? documentHeight : bodyHeight
|
||||
}
|
||||
|
||||
const finalHeight = clientHeight - num - FIXED_HEIGHT
|
||||
return finalHeight > 0 ? finalHeight : -finalHeight
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
name: 'AttrForm',
|
||||
components: { BaseTable },
|
||||
props: {
|
||||
isDetail: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/** subtable 需要设置的属性 */
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
tableConfigs: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
/** 表单提交需要的属性 */
|
||||
relatedId: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: null
|
||||
},
|
||||
relatedField: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
calcMaxHeight,
|
||||
showAddAttr: false,
|
||||
dataList: [],
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
totalPage: 0,
|
||||
AttrForm: {}, // 需动态设置
|
||||
AttrFormRules: {} // 需动态设置
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
attrFormFields() {
|
||||
const _ = this.tableConfigs.filter(item => item.formField)
|
||||
/** 顺带配置 AttrForm */
|
||||
_.forEach(item => {
|
||||
this.$set(this.AttrForm, [item.prop], '')
|
||||
})
|
||||
|
||||
this.$set(this.AttrForm, 'id', null)
|
||||
return _
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getDataList()
|
||||
/** 设置 AttrForm 的 rules */
|
||||
for (const config of this.tableConfigs) {
|
||||
if (config.rules) {
|
||||
this.$set(this.AttrFormRules, [config.prop], config.rules)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filterTableConfigs() {
|
||||
if (this.isDetail) {
|
||||
/** 如果是查看详情,就屏蔽操作列 */
|
||||
return this.tableConfigs.filter(opt => opt.prop !== 'operations')
|
||||
}
|
||||
return this.tableConfigs
|
||||
},
|
||||
/** init dataform */
|
||||
initAttrForm() {
|
||||
Object.entries(this.AttrForm).forEach(([key, value]) => {
|
||||
if (typeof value === 'object' || typeof value === 'number') {
|
||||
this.AttrForm[key] = null
|
||||
} else if (Array.isArray(value)) {
|
||||
this.AttrForm[key] = []
|
||||
} else {
|
||||
this.AttrForm[key] = ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** requests */
|
||||
getDataList() {
|
||||
this.dataListLoading = true
|
||||
// 获取动态属性列表
|
||||
this.$http({
|
||||
url: this.$http.adornUrl(`${this.url}/page`),
|
||||
method: 'get',
|
||||
params: this.$http.adornParams({
|
||||
page: this.pageIndex,
|
||||
limit: this.pageSize,
|
||||
[this.relatedField]: this.relatedId
|
||||
// order: 'asc/desc',
|
||||
// orderField: 'name'
|
||||
})
|
||||
}).then(({ data: res }) => {
|
||||
if (res && res.code === 0) {
|
||||
this.dataList = res.data.list
|
||||
this.totalPage = res.data.total
|
||||
} else {
|
||||
this.dataList = []
|
||||
this.totalPage = 0
|
||||
}
|
||||
this.dataListLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** handlers */
|
||||
handleOperations({ type, data: id }) {
|
||||
switch (type) {
|
||||
case 'edit':
|
||||
{
|
||||
this.showAddAttr = true
|
||||
this.$nextTick(() => {
|
||||
this.$http.get(this.$http.adornUrl(`${this.url}/${id}`)).then(({ data: res }) => {
|
||||
if (res && res.code === 0 && res.data) {
|
||||
const neededFields = [...this.attrFormFields.map(item => item.prop), 'id']
|
||||
const filtered = pick(res.data, neededFields)
|
||||
for (let field of neededFields) {
|
||||
this.AttrForm[field] = filtered[field]
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'delete':
|
||||
return this.deleteHandle(id)
|
||||
}
|
||||
},
|
||||
deleteHandle(id) {
|
||||
var ids = id ? [id] : []
|
||||
|
||||
this.$confirm(`${i18n.t('prompt.info', { handle: id ? i18n.t('delete').toLowerCase() : i18n.t('deleteBatch').toLowerCase() })}`, i18n.t('prompt.title'), {
|
||||
confirmButtonText: i18n.t('confirm'),
|
||||
cancelButtonText: i18n.t('cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$http({
|
||||
url: this.$http.adornUrl(this.url),
|
||||
method: 'delete',
|
||||
data: this.$http.adornData(ids, false, 'raw')
|
||||
}).then(({ data }) => {
|
||||
if (data && data.code === 0) {
|
||||
this.$message({
|
||||
message: i18n.t('prompt.success'),
|
||||
type: 'success',
|
||||
duration: 1500,
|
||||
onClose: () => {
|
||||
this.getDataList()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.error(data.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
handleCloseAttrForm() {
|
||||
this.showAddAttr = false
|
||||
this.initAttrForm()
|
||||
},
|
||||
|
||||
handleSaveAttrForm() {
|
||||
this.$refs['AttrForm'].validate(valid => {
|
||||
if (valid) {
|
||||
this.$http({
|
||||
// url: this.$http.adornUrl(`${this.url}/${!this.AttrForm.id ? '' : this.AttrForm.id}`),
|
||||
url: this.$http.adornUrl(this.url),
|
||||
method: this.AttrForm.id ? 'put' : 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify({ ...this.AttrForm, [this.relatedField]: this.relatedId })
|
||||
}).then(({ data }) => {
|
||||
if (data && data.code === 0) {
|
||||
this.$message({
|
||||
message: i18n.t('prompt.success'),
|
||||
type: 'success',
|
||||
duration: 1500,
|
||||
onClose: () => {
|
||||
this.showAddAttr = false
|
||||
this.getDataList()
|
||||
this.initAttrForm()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.error(data.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 每页数
|
||||
sizeChangeHandle(val) {
|
||||
this.pageSize = val
|
||||
this.pageIndex = 1
|
||||
this.getDataList()
|
||||
},
|
||||
// 当前页
|
||||
currentChangeHandle(val) {
|
||||
this.pageIndex = val
|
||||
this.getDataList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.attr-form >>> .el-form .el-form-item__label {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
30
src/components/BaseAgreeOrNot.vue
Normal file
30
src/components/BaseAgreeOrNot.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<!-- 同意或不同意组件,点击不同意的时候出现额外的输入框说明原因 -->
|
||||
<el-row>
|
||||
<el-button type="primary" @click="doAgree">同意</el-button>
|
||||
<el-button @click="dontAgree">不同意</el-button>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: '',
|
||||
props: {},
|
||||
emits: ['not-agree', 'agree'],
|
||||
data() {
|
||||
return {
|
||||
reason: 'dont agree reason',
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
doAgree() {
|
||||
this.$emit('agree');
|
||||
},
|
||||
dontAgree() {
|
||||
this.$emit('not-agree', this.reason);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
649
src/components/BaseDialog.vue
Normal file
649
src/components/BaseDialog.vue
Normal file
@@ -0,0 +1,649 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
class="super-flexible-dialog"
|
||||
:title="isDetail ? title.detail : !dataForm.id ? title.add : title.edit"
|
||||
:visible.sync="visible"
|
||||
@close="handleClose"
|
||||
:distory-on-close="true"
|
||||
:close-on-click-modal="false">
|
||||
<div style="max-height: 60vh; overflow-y: scroll; overflow-x: hidden">
|
||||
<el-form ref="dataForm" :model="dataForm" :rules="dataFormRules">
|
||||
<!-- 如果需要更精细一点的布局,可以根据配置项实现地再复杂一点,但此处暂时全部采用一行两列布局 -->
|
||||
<el-row v-for="n in rows" :key="n" :gutter="20">
|
||||
<el-col v-for="c in COLUMN_PER_ROW" :key="`${n}+'col'+${c}`" :span="getSpan(n, c)">
|
||||
<!-- <el-col v-for="c in COLUMN_PER_ROW" :key="`${n}+'col'+${c}`" :span="24 / COLUMN_PER_ROW"> -->
|
||||
<!-- :class="{ 'hidden-input': configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].hidden }" -->
|
||||
<el-form-item
|
||||
v-if="configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)]"
|
||||
:prop="configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name"
|
||||
:key="`${n}-col-${c}-item`"
|
||||
:label="getLabel(n, c)">
|
||||
<!-- 暂时先不实现部分输入方式 -->
|
||||
<el-input
|
||||
v-if="getType(n, c) === 'input'"
|
||||
:placeholder="getPlaceholder(n, c)"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
clearable
|
||||
:disabled="isDetail" />
|
||||
<el-radio
|
||||
v-if="getType(n, c) === 'radio'"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
:disabled="isDetail" />
|
||||
<el-checkbox
|
||||
v-if="getType(n, c) === 'check'"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
:disabled="isDetail" />
|
||||
<el-select
|
||||
v-if="getType(n, c) === 'select'"
|
||||
:placeholder="getPlaceholder(n, c)"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
clearable
|
||||
:disabled="isDetail"
|
||||
@change="emitSelectChange(configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name, $event)">
|
||||
<el-option
|
||||
v-for="opt in configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].options"
|
||||
:key="opt.label + Math.random()"
|
||||
:label="opt.label"
|
||||
:value="opt.value" />
|
||||
</el-select>
|
||||
<el-switch
|
||||
v-if="getType(n, c) === 'switch'"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
:disabled="isDetail" />
|
||||
<el-cascader
|
||||
v-if="getType(n, c) === 'cascader'"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
:options="configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].options"
|
||||
:props="configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].props"
|
||||
:disabled="isDetail"
|
||||
clearable />
|
||||
<el-time-select
|
||||
v-if="getType(n, c) === 'time'"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
:disabled="isDetail" />
|
||||
<el-date-picker
|
||||
v-if="getType(n, c) === 'date'"
|
||||
v-bind="configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].props"
|
||||
:placeholder="getPlaceholder(n, c)"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
:disabled="isDetail" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- extra components , like Markdown or RichEdit -->
|
||||
<template v-if="configs.extraComponents && configs.extraComponents.length > 0">
|
||||
<el-form-item
|
||||
v-for="(ec, index) in configs.extraComponents"
|
||||
:key="ec.name + index"
|
||||
:label="ec.label"
|
||||
class="extra-components">
|
||||
<component
|
||||
style="margin-top: 40px"
|
||||
v-if="ec.hasModel"
|
||||
:is="ec.component"
|
||||
v-bind="ec.props"
|
||||
v-model="dataForm[ec.name]"
|
||||
@ready="handleEditorReady"
|
||||
:read-only="isDetail" />
|
||||
<!-- <component v-if="ec.hasModel" :is="ec.component" v-bind="ec.props" v-model="dataForm[ec.name]" /> -->
|
||||
<component
|
||||
v-else
|
||||
:is="ec.component"
|
||||
v-bind="ec.props"
|
||||
@uploader-update-filelist="handleUploadListUpdate($event, ec.props.extraParams.typeCode)"
|
||||
:uploader-inject-file-list="/*用于设备分流的*/ fileList[ec.props.extraParams.typeCode]"
|
||||
:read-only="isDetail" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
|
||||
<!-- <template v-if="dataForm.id && configs.subtable">
|
||||
<attr-form :related-id="dataForm.id" v-bind="configs.subtable" :is-detail="isDetail" />
|
||||
</template> -->
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<template v-for="(operate, index) in configs.operations">
|
||||
<!-- {{ operate.name | btnNameFilter }} -->
|
||||
<el-button
|
||||
v-if="
|
||||
!isDetail &&
|
||||
(operate.showAlways ||
|
||||
(((dataForm.id && operate.showOnEdit) || (!dataForm.id && !operate.showOnEdit)) &&
|
||||
(operate.permission ? $hasPermission(operate.permission) : true)))
|
||||
"
|
||||
:key="`operate-${index}`"
|
||||
:type="btnType[operate.name]"
|
||||
@click="handleClick(operate)"
|
||||
>{{ btnName[operate.name] }}</el-button
|
||||
>
|
||||
</template>
|
||||
<el-button v-if="isDetail" @click="handleClick({ name: 'cancel' })">{{ $t('cancel') }}</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import CKEditor from 'ckeditor4-vue'
|
||||
// import AttrForm from './AttrForm'
|
||||
// import { pick } from 'lodash/object'
|
||||
import { pick as __pick } from '@/utils/filters';
|
||||
// import i18n from '@/i18n'
|
||||
import $http from '@/utils/request';
|
||||
|
||||
const i18n = {
|
||||
t: (a) => a,
|
||||
};
|
||||
|
||||
// 标题 for i18n
|
||||
const title = {
|
||||
// detail: i18n.t('detail'),
|
||||
// add: i18n.t('add'),
|
||||
// edit: i18n.t('edit'),
|
||||
detail: '详情',
|
||||
add: '添加',
|
||||
edit: '编辑',
|
||||
};
|
||||
|
||||
// 或者也可以改造成自定义颜色:
|
||||
const btnType = {
|
||||
save: 'success',
|
||||
update: 'primary',
|
||||
reset: 'text',
|
||||
// cancel: 'text'
|
||||
// add more...
|
||||
};
|
||||
|
||||
const btnName = {
|
||||
save: '保存',
|
||||
update: '更新',
|
||||
reset: '重置',
|
||||
cancel: '取消',
|
||||
// for i18n
|
||||
// save: i18n.t('save'),
|
||||
// update: i18n.t('update'),
|
||||
// reset: i18n.t('reset'),
|
||||
// cancel: i18n.t('cancel'),
|
||||
// add more...
|
||||
};
|
||||
|
||||
// 每行的列数
|
||||
const COLUMN_PER_ROW = 2;
|
||||
|
||||
export default {
|
||||
name: 'AddOrUpdateDialog',
|
||||
// components: { AttrForm },
|
||||
props: {
|
||||
configs: {
|
||||
type: Object,
|
||||
default: () => ({}), // 此处省去类型检查,使用者自行注意就好
|
||||
},
|
||||
url: {
|
||||
type: Object,
|
||||
default: () => ({}), // 此处省去类型检查,使用者自行注意就好
|
||||
},
|
||||
},
|
||||
filters: {
|
||||
nameFilter: function (name) {
|
||||
if (!name) return null;
|
||||
// for i18n
|
||||
const defaultNames = {
|
||||
name: '名称',
|
||||
code: '编码',
|
||||
remark: '备注',
|
||||
description: '描述',
|
||||
specifications: '规格',
|
||||
// name: i18n.t('name'),
|
||||
// code: i18n.t('code'),
|
||||
// remark: i18n.t('remark'),
|
||||
// description: i18n.t('desc'),
|
||||
// specifications: i18n.t('prod.spec'),
|
||||
// add more...
|
||||
};
|
||||
|
||||
return defaultNames[name];
|
||||
},
|
||||
},
|
||||
// provide() {
|
||||
// return {
|
||||
// _df: this.dataForm
|
||||
// }
|
||||
// },
|
||||
data() {
|
||||
return {
|
||||
COLUMN_PER_ROW,
|
||||
title,
|
||||
/** 按钮相关属性 */
|
||||
btnName,
|
||||
btnType,
|
||||
defaultNames: {
|
||||
name: '名称',
|
||||
code: '编码',
|
||||
remark: '备注',
|
||||
description: '描述',
|
||||
specifications: '规格',
|
||||
// add more...
|
||||
// name: i18n.t('name'),
|
||||
// code: i18n.t('code'),
|
||||
// remark: i18n.t('remark'),
|
||||
// description: i18n.t('desc'),
|
||||
// specifications: i18n.t('prod.spec'),
|
||||
// add more...
|
||||
},
|
||||
defaultPlaceholders: {}, // 自动根据 defaultNames 计算得来
|
||||
/** 表单相关属性 */
|
||||
visible: false,
|
||||
isEdit: false,
|
||||
isDetail: false,
|
||||
dataForm: {},
|
||||
dataFormRules: {},
|
||||
tempForm: [], // 临时保存自动生成的code,或其他数据
|
||||
shouldWait: null,
|
||||
fileForm: {}, // 文件上传分流用、合并用的表单,根据 typeCode 进行分流,在请求时合并
|
||||
fileList: {}, // 文件加载时分流,依据 typeCode
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
rows() {
|
||||
// 本组件只实现了'一行两列'的表单布局
|
||||
return Math.ceil(this.configs.fields.length / COLUMN_PER_ROW);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
/** load lang */
|
||||
// CKEditor.load()
|
||||
// console.log('lang', CKEditor.component.props.config.defaultLanguage = 'en' )
|
||||
},
|
||||
mounted() {
|
||||
/** 计算 defaultPlaceholders */
|
||||
// const prefix = i18n.t('hints.input');
|
||||
const prefix = '请输入';
|
||||
Object.entries(this.defaultNames).map(([key, value]) => {
|
||||
this.defaultPlaceholders[key] = prefix + value;
|
||||
});
|
||||
|
||||
/** 转换 configs.fields 的结构,把纯字符串转为对象 */
|
||||
this.$nextTick(() => {
|
||||
this.configs.fields = this.configs.fields.map((item) => {
|
||||
if (typeof item === 'string') {
|
||||
return { name: item };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
/** 动态设置dataForm字段 */
|
||||
this.configs.fields.forEach((item) => {
|
||||
this.$set(this.dataForm, [item.name], '');
|
||||
|
||||
/** select 的默认值设置 */
|
||||
if (item.type === 'select') {
|
||||
const opts = item.options || [];
|
||||
const dft = opts.find((item) => item.default || false);
|
||||
if (dft) {
|
||||
this.$set(this.dataForm, [item.name], dft.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.api) {
|
||||
/** 自动请求并填充 */
|
||||
// or this.shouldWaitPool = []
|
||||
this.shouldWait =
|
||||
// this.$http({
|
||||
// url: this.$http.adornUrl(item.api),
|
||||
// method: 'POST', // 也可以改成动态决定
|
||||
// })
|
||||
$http
|
||||
.get(item.api)
|
||||
// .then(({ data: res }) => {
|
||||
.then((res) => {
|
||||
if (res && res.code === 0) {
|
||||
// this.dataForm[item.name] = res.data // <=== 此处需要对接口
|
||||
this.tempForm.push({ name: item.name, data: res.data });
|
||||
}
|
||||
});
|
||||
} // end if (item.api)
|
||||
|
||||
// 如果有 relatedField,就需要在当前item的数据加载后,刷新 relatedField 的列表
|
||||
if (item.relatedField) {
|
||||
this.$watch(
|
||||
function () {
|
||||
return this.dataForm[item.name];
|
||||
},
|
||||
function (val, old) {
|
||||
if (val && val !== old) {
|
||||
this.$emit('select-change', { name: item.name, id: val });
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
}
|
||||
|
||||
if (item.required) {
|
||||
const requiredRule = {
|
||||
required: true,
|
||||
message: '必填项',
|
||||
// message: i18n.t('validate.required'),
|
||||
// trigger: 'change'
|
||||
trigger: 'blur',
|
||||
};
|
||||
/** 检查是否已经存在该字段的规则 */
|
||||
const exists = this.dataFormRules[item.name] || null;
|
||||
/** 设置验证规则 */
|
||||
if (exists) {
|
||||
const unset = true;
|
||||
for (const rule of exists) {
|
||||
if (rule.required) unset = false;
|
||||
}
|
||||
if (unset) {
|
||||
exists.push(requiredRule);
|
||||
}
|
||||
} else {
|
||||
/** 不存在已有规则 */
|
||||
this.$set(this.dataFormRules, [item.name], [requiredRule]);
|
||||
}
|
||||
} // end if (item.required)
|
||||
|
||||
if (item.rules) {
|
||||
const exists = this.dataFormRules[item.name] || null;
|
||||
if (exists) {
|
||||
// 浅拷贝过去
|
||||
exists.push(...item.rules);
|
||||
} else {
|
||||
this.$set(this.dataFormRules, [item.name], [...item.rules]);
|
||||
}
|
||||
} // end if (item.rules)
|
||||
});
|
||||
|
||||
/** 计算默认值 */
|
||||
function calDefault(type) {
|
||||
switch (type) {
|
||||
case 'array':
|
||||
return [];
|
||||
// more case...
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
/** 检查是否需要额外的组件 */
|
||||
this.configs.extraComponents &&
|
||||
this.configs.extraComponents.forEach((item) => {
|
||||
// if (Object.hasOwn(this.dataForm, [item.name])) {
|
||||
if (this.dataForm.hasOwnProperty(item.name)) {
|
||||
return;
|
||||
} else {
|
||||
this.$set(this.dataForm, [item.name], calDefault(item.fieldType));
|
||||
}
|
||||
});
|
||||
|
||||
/** 单独设置 id */
|
||||
this.$set(this.dataForm, 'id', null);
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
getSpan(n, c) {
|
||||
const opt = this.configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)];
|
||||
return opt && opt.span ? opt.span : 24 / COLUMN_PER_ROW;
|
||||
},
|
||||
|
||||
getLabel(n, c) {
|
||||
const opt = this.configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)];
|
||||
if (opt) {
|
||||
// if opt is valid
|
||||
return opt.label ? opt.label : this.defaultNames[opt.name];
|
||||
}
|
||||
},
|
||||
|
||||
getPlaceholder(n, c) {
|
||||
if (this.isDetail) {
|
||||
/** 如果是详情,就不展示 提示文本 */
|
||||
return '';
|
||||
}
|
||||
|
||||
const opt = this.configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)];
|
||||
if (opt) {
|
||||
// if opt is valid
|
||||
return opt.placeholder
|
||||
? opt.placeholder
|
||||
: this.defaultPlaceholders[opt.name]
|
||||
? this.defaultPlaceholders[opt.name]
|
||||
: opt.label
|
||||
? (opt.type === 'select' ? '请选择' : '请输入') + opt.label
|
||||
: // ? (opt.type === 'select' ? i18n.t('choose') : i18n.t('hints.input')) + opt.label
|
||||
null;
|
||||
|
||||
// : opt.type === 'select'
|
||||
// ? i18n.t('choose')
|
||||
// : '请输入'
|
||||
}
|
||||
},
|
||||
|
||||
getType(n, c) {
|
||||
const opt = this.configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)];
|
||||
if (opt) {
|
||||
if (!opt.type || ['input', 'number' /** add more.. */].includes(opt.type)) {
|
||||
return 'input';
|
||||
} else if (['select' /** add more.. */].includes(opt.type)) {
|
||||
return 'select';
|
||||
} else if (['cascader'].includes(opt.type)) {
|
||||
return 'cascader';
|
||||
} else if (['date'].includes(opt.type)) {
|
||||
return 'date';
|
||||
}
|
||||
// add more...
|
||||
} else {
|
||||
return 'input';
|
||||
}
|
||||
},
|
||||
|
||||
init(id, isdetail = false) {
|
||||
this.isDetail = isdetail;
|
||||
this.visible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].resetFields();
|
||||
this.dataForm.id = id || null;
|
||||
if (this.dataForm.id) {
|
||||
// this.$http({
|
||||
// url: this.$http.adornUrl(`${this.configs.infoUrl}/${this.dataForm.id}`),
|
||||
// method: 'get'
|
||||
// })
|
||||
$http.get(this.url.base + `/${this.dataForm.id}`).then((res) => {
|
||||
console.log('[base dialog init] ', res);
|
||||
if (res && res.code === 0) {
|
||||
const dataFormKeys = Object.keys(this.dataForm);
|
||||
console.log('keys ===> ', dataFormKeys);
|
||||
// console.log('data form keys: ', dataFormKeys, pick(res.data, dataFormKeys))
|
||||
this.dataForm = __pick(res.data, dataFormKeys);
|
||||
console.log('pick(res.data, dataFormKeys) ===> ', __pick(res.data, dataFormKeys));
|
||||
// LABEL: FILE_RELATED
|
||||
/** 对文件下载进行分流 */
|
||||
this.fileList = {};
|
||||
if (this.dataForm.files) {
|
||||
// console.log('files: ', this.dataForm.files)
|
||||
this.dataForm.files.forEach((file) => {
|
||||
// const fileName = file.fileUrl.split('/').pop()
|
||||
/** [1] 处理 fileList */
|
||||
// if (Object.hasOwn(this.fileList, file.typeCode)) {
|
||||
if (this.fileList.hasOwnProperty(file.typeCode)) {
|
||||
/** 已存在 */
|
||||
// this.fileList[file.typeCode].push({ id: file.id, name: fileName, typeCode: file.typeCode })
|
||||
this.fileList[file.typeCode].push(file);
|
||||
} else {
|
||||
// this.fileList[file.typeCode] = [{ id: file.id, name: fileName, typeCode: file.typeCode }]
|
||||
this.fileList[file.typeCode] = [file];
|
||||
}
|
||||
|
||||
/** [2] 处理 fileForm */
|
||||
// if (Object.hasOwn(this.fileForm, file.typeCode)) {
|
||||
if (this.fileForm.hasOwnProperty(file.typeCode)) {
|
||||
this.fileForm[file.typeCode].push(file.id);
|
||||
} else {
|
||||
this.fileForm[file.typeCode] = [file.id];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
/** 如果不是编辑,就填充自动生成的数据 */
|
||||
if (this.shouldWait)
|
||||
this.shouldWait.then(() => {
|
||||
if (this.tempForm.length) {
|
||||
// console.log('create new, tempform', JSON.stringify(this.tempForm.length))
|
||||
this.tempForm.forEach((item) => {
|
||||
// console.log('item data', item.data)
|
||||
this.dataForm[item.name] = item.data;
|
||||
});
|
||||
// console.log('create new, dataform', JSON.stringify(this.dataForm))
|
||||
}
|
||||
this.shouldWait = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
emitSelectChange(name, id) {
|
||||
const currentField = this.configs.fields.find((item) => item.name === name);
|
||||
if (currentField.relatedField) {
|
||||
this.dataForm[currentField.relatedField] = null;
|
||||
}
|
||||
this.$emit('select-change', { name, id });
|
||||
},
|
||||
|
||||
handleEditorReady(val) {},
|
||||
|
||||
handleClick(btn) {
|
||||
/** 提取url */
|
||||
// const urls = {};
|
||||
// this.configs.operations.map((item) => {
|
||||
// urls[item.name] = {};
|
||||
// urls[item.name].url = item.url;
|
||||
// urls[item.name].extraFields = item.extraFields || {};
|
||||
// });
|
||||
/** 操作 */
|
||||
switch (btn.name) {
|
||||
case 'save':
|
||||
case 'update':
|
||||
/** 需要验证表单的操作 */
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
/** 对于文件上传的单独处理(合并处理) */
|
||||
if (Object.keys(this.fileForm).length) {
|
||||
// LABEL: FILE_RELATED
|
||||
let fileIds = [];
|
||||
for (const [key, item] of Object.entries(this.fileForm)) {
|
||||
if (Array.isArray(item)) {
|
||||
fileIds = fileIds.concat(item);
|
||||
} else {
|
||||
console.error('handleClick(): 上传文件数组类型不正确');
|
||||
}
|
||||
}
|
||||
this.$set(this.dataForm, 'fileIds', fileIds);
|
||||
}
|
||||
|
||||
// console.log('before send: ', this.dataForm)
|
||||
|
||||
// this.$http({
|
||||
// url: this.$http.adornUrl(urls[btn.name].url),
|
||||
// method: btn.name === 'save' ? 'POST' : 'PUT',
|
||||
// data: { ...this.dataForm, ...urls[btn.name].extraFields },
|
||||
// })
|
||||
$http[btn.name === 'save' ? 'post' : 'put'](btn.url, { ...this.dataForm, ...btn.extraFields })
|
||||
.then((res) => {
|
||||
// .then(({ data: res }) => {
|
||||
console.log('save res: ', res, btn.name, this.dataForm);
|
||||
if (res && res.code === 0) {
|
||||
this.$message({
|
||||
message: '操作成功!',
|
||||
// message: i18n.t('prompt.success'),
|
||||
// message: btn.name === 'save' ? i18n.t('prompt.success') : '更新成功!',
|
||||
type: 'success',
|
||||
duration: 1500,
|
||||
onClose: () => {
|
||||
this.$emit('refreshDataList');
|
||||
this.visible = false;
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$message({
|
||||
message: err,
|
||||
type: 'error',
|
||||
duration: 2000,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
return;
|
||||
case 'reset':
|
||||
for (const key of Object.keys(this.dataForm)) {
|
||||
if (typeof this.dataForm[key] === 'string') {
|
||||
this.dataForm[key] = '';
|
||||
} else if (this.dataForm[key] instanceof Array) {
|
||||
this.dataForm[key].splice(0);
|
||||
} else {
|
||||
this.dataForm[key] = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'cancel':
|
||||
this.handleClose();
|
||||
// add more..
|
||||
}
|
||||
},
|
||||
|
||||
// LABEL: FILE_RELATED
|
||||
handleUploadListUpdate(filelist, typeCode = 'DefaultTypeCode') {
|
||||
// console.log('before handleUploadListUpdate(): ', JSON.parse(JSON.stringify(this.fileForm)))
|
||||
// 设备类型 typeCode: EquipmentTypeFile
|
||||
// 设备信息 typeCode: EquipmentInfoFile | EquipmentInfoImage
|
||||
|
||||
// 原先写法:直接更新 dataForm 对象,不适用于有多个上传组件的需求
|
||||
// this.$set(
|
||||
// this.dataForm,
|
||||
// 'fileIds',
|
||||
// filelist.map(item => item.id)
|
||||
// )
|
||||
// console.log('handleUploadListUpdate(): ', this.dataForm)
|
||||
// 现更改为分流写法
|
||||
this.$set(
|
||||
this.fileForm,
|
||||
typeCode,
|
||||
filelist.map((item) => item.id)
|
||||
);
|
||||
// console.log('after handleUploadListUpdate(): ', this.fileForm)
|
||||
},
|
||||
|
||||
handleClose() {
|
||||
this.$emit('destory-dialog');
|
||||
this.visible = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.super-flexible-dialog >>> .el-select,
|
||||
.super-flexible-dialog >>> .el-cascader {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.super-flexible-dialog >>> ::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.super-flexible-dialog >>> ::-webkit-scrollbar-thumb {
|
||||
width: 4px;
|
||||
border-radius: 4px;
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
.super-flexible-dialog >>> .hidden-input {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
19
src/components/BaseDrawer.vue
Normal file
19
src/components/BaseDrawer.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<!-- 基本抽屉 -->
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: '',
|
||||
props: {},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
120
src/components/BaseListTable.vue
Normal file
120
src/components/BaseListTable.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<!-- 这里单纯的配置表格就好了-->
|
||||
<template>
|
||||
<div class="base-list-table w-full">
|
||||
<el-table :data="tableData" v-bind="tableConfig" ref="base-list-table">
|
||||
<!-- @cell-mouse-enter="(row, col, cell, event) => $emit('cell-mouse-enter', row, col, cell, event)"> -->
|
||||
<!-- @cell-mouse-leave="(row, col, cell, event) => $emit('cell-mouse-leave', row, col, cell, event)"> -->
|
||||
<!-- 表格头定义 -->
|
||||
<template v-for="(head, idx) in columnConfig">
|
||||
<!-- 索引列 -->
|
||||
<el-table-column
|
||||
:key="idx"
|
||||
v-if="head.type"
|
||||
:type="head.type"
|
||||
:label="head.label || head.name || ''"
|
||||
:header-align="head.align || 'center'"
|
||||
:align="head.align || 'center'"
|
||||
:width="head.width || 50"
|
||||
:index="
|
||||
head.type === 'index'
|
||||
? (val) => {
|
||||
return val + 1 + (page - 1) * size;
|
||||
}
|
||||
: null
|
||||
"
|
||||
v-bind="head.more"></el-table-column>
|
||||
<!-- 普通的表头 -->
|
||||
<el-table-column
|
||||
v-else
|
||||
:key="idx + 'else'"
|
||||
:label="head.label ? head.label : head.name"
|
||||
:prop="head.prop || null"
|
||||
:width="head.width || null"
|
||||
:min-width="head.minWidth || null"
|
||||
:fixed="head.fixed || null"
|
||||
:show-overflow-tooltip="head.showOverflowTooltip || true"
|
||||
:tooltip-effect="head.tooltipEffect || 'light'"
|
||||
filter-placement="top"
|
||||
:align="head.align || null"
|
||||
v-bind="head.more">
|
||||
<!-- 子组件 -->
|
||||
<template v-if="head.prop" slot-scope="scope">
|
||||
<component
|
||||
v-if="head.subcomponent"
|
||||
:is="head.subcomponent"
|
||||
:key="idx + 'sub'"
|
||||
:inject-data="{ ...scope.row, head }"
|
||||
@emit-data="handleSubEmitData" />
|
||||
<!-- 直接展示数据或应用过滤器 -->
|
||||
<span v-else>{{ scope.row[head.prop] | commonFilter(head.filter) }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 多级表头 -->
|
||||
<template v-if="!head.prop && head.children">
|
||||
<TableHead
|
||||
v-for="(subhead, subindex) in head.children"
|
||||
:key="'subhead-' + idx + '-subindex-' + subindex"
|
||||
:opt="subhead" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TableHead from './TableHead.vue';
|
||||
import $http from '@/utils/request'
|
||||
// TODO:
|
||||
// 1. 表格拖拽开启/关闭
|
||||
// 2. 表格的样式'
|
||||
// 3. more...
|
||||
|
||||
export default {
|
||||
name: 'BaseListTable',
|
||||
components: { TableHead },
|
||||
filters: {
|
||||
commonFilter: (source, filterType = (a) => a) => {
|
||||
return filterType(source);
|
||||
},
|
||||
},
|
||||
props: {
|
||||
tableConfig: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
columnConfig: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
tableData: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
}
|
||||
},
|
||||
inject: ['urls'],
|
||||
data() {
|
||||
return {
|
||||
page: 1,
|
||||
size: 20, // 默认20
|
||||
dataList: []
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 'props.tableData': {
|
||||
// handler: () => {
|
||||
// this.$refs['base-list-table'].doLayout();
|
||||
// },
|
||||
// immediate: true,
|
||||
// },
|
||||
},
|
||||
methods: {
|
||||
handleSubEmitData(payload) {
|
||||
console.log('[component] BaseListTable handleSubEmitData(): ', payload);
|
||||
this.$emit('operate-event', payload);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
167
src/components/BaseSearchForm.vue
Normal file
167
src/components/BaseSearchForm.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<el-row class="base-search-form rounded">
|
||||
<el-form :inline="true" :model="searchForm" :rules="headConfig.rules">
|
||||
<!-- <el-col :span="opt.span ? +opt.span : 4" v-for="opt in options" :key="opt.id"> -->
|
||||
<el-form-item
|
||||
v-for="(opt, index) in headConfig.fields"
|
||||
:key="opt.prop"
|
||||
:label="opt.label ? opt.label : null"
|
||||
:prop="'' + index"
|
||||
:rules="opt.elconfig?.rules ? opt.elconfig.rules : undefined"
|
||||
>
|
||||
<el-input
|
||||
v-if="opt.input"
|
||||
v-model="searchForm[index]"
|
||||
v-bind="opt.elconfig"
|
||||
/>
|
||||
<el-select
|
||||
v-if="opt.select"
|
||||
v-model="searchForm[index]"
|
||||
v-bind="opt.elconfig"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in opt.select"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-if="opt.timerange"
|
||||
v-model="searchForm[index]"
|
||||
v-bind="opt.elconfig"
|
||||
/>
|
||||
<el-upload
|
||||
v-if="opt.upload"
|
||||
:key="'upload_' + Math.random().toString()"
|
||||
class="inline-block pl-3"
|
||||
action="https://jsonplaceholder.typicode.com/posts/"
|
||||
>
|
||||
<el-button type="primary">上传文件</el-button>
|
||||
</el-upload>
|
||||
<el-button
|
||||
v-if="opt.button && (!opt.button.permission || $hasPermission(opt.button.permission))"
|
||||
:key="'button' + Math.random().toString()"
|
||||
:type="opt.button.type"
|
||||
@click="handleBtnClick(opt.button.name)"
|
||||
>{{ opt.button.name }}</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "",
|
||||
props: {
|
||||
headConfig: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchForm: {
|
||||
// 最大参数个数:10个,不够则在这里加
|
||||
0: null, // 参数一
|
||||
1: null,
|
||||
2: null,
|
||||
3: null,
|
||||
4: null,
|
||||
5: null,
|
||||
6: null,
|
||||
7: null,
|
||||
8: null,
|
||||
9: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
searchForm: {
|
||||
handler: (val) => {
|
||||
console.log("new val", val);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
created() {},
|
||||
mounted() {
|
||||
console.log(
|
||||
"head form config",
|
||||
JSON.parse(JSON.stringify(this.headConfig))
|
||||
);
|
||||
this.headConfig.fields.forEach((field, index) => {
|
||||
if (field.default) {
|
||||
// default 必须有个 value 属性!哪怕是 input 输入框
|
||||
this.searchForm[index] = field.default.value;
|
||||
}
|
||||
|
||||
// 更新选项列表
|
||||
if (!field.watch && field.fn && typeof field.fn === "function") {
|
||||
// 设置自身的选项列表
|
||||
field.fn().then((res) => {
|
||||
field.select = res.map((_) => {
|
||||
// 找到默认项
|
||||
if (_.default) {
|
||||
this.searchForm[index] = _.value;
|
||||
}
|
||||
return _;
|
||||
});
|
||||
console.log("[update] 更新选项列表", res, field.select);
|
||||
});
|
||||
}
|
||||
|
||||
// 模拟请求:
|
||||
const axios = function(url, data) {
|
||||
return new Promise((resolve) => {
|
||||
resolve("success");
|
||||
});
|
||||
};
|
||||
|
||||
// 如果需要监听关联字段
|
||||
if (field.watch) {
|
||||
const { index: innerIdx, condition } = field.watch;
|
||||
console.log(
|
||||
"=====field.watch=====",
|
||||
innerIdx,
|
||||
this.searchForm[innerIdx],
|
||||
this.headConfig.fields[innerIdx].default
|
||||
);
|
||||
// 设置监听器
|
||||
this.$watch(
|
||||
() => this.searchForm[innerIdx],
|
||||
(val) => {
|
||||
const queryParams = { [condition]: val };
|
||||
// field.watch.condition.forEach(c => {
|
||||
// queryParams
|
||||
// })
|
||||
axios(field.url, queryParams).then((res) => {
|
||||
console.log("[==>] 更新有前置条件的字段!!!", queryParams, res);
|
||||
// 此处是外部的 index
|
||||
this.searchForm[index] = Math.floor(Math.random() * 10);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
console.log("xxxxxxxxxx", this.searchForm);
|
||||
// 如果此时已经有默认值了,就立马根据这个默认值来发起请求
|
||||
if (this.searchForm[innerIdx]) {
|
||||
// TODO: 这个判断好像不太需要...
|
||||
console.log("TODO: 这个判断好像不太需要...");
|
||||
} else {
|
||||
console.log("TODO: 监听的字段还没来得及设置值呢...");
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
handleBtnClick(name) {
|
||||
this.$emit("btn-click", { btnName: name, payload: null });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
206
src/components/BaseTable.vue
Normal file
206
src/components/BaseTable.vue
Normal file
@@ -0,0 +1,206 @@
|
||||
<!--
|
||||
* @Date: 2020-12-14 09:07:03
|
||||
* @LastEditors: gtz
|
||||
* @LastEditTime: 2022-06-13 08:59:21
|
||||
* @FilePath: \mt-bus-fe\src\components\BaseTable\index.vue
|
||||
* @Description:
|
||||
-->
|
||||
<template>
|
||||
<div class="">
|
||||
<el-table
|
||||
:header-cell-style="{
|
||||
background: '#FAFAFA',
|
||||
color: '#606266',
|
||||
height: '40px',
|
||||
}"
|
||||
:data="renderData"
|
||||
:show-header="showHeader"
|
||||
:border="border"
|
||||
fit
|
||||
highlight-current-rows
|
||||
style="width: 100%"
|
||||
@row-click="handleRowClick"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<!-- :max-height="height ? height : tableHeight(325)" -->
|
||||
<el-table-column v-if="sbox" type="selection" width="55" fixed />
|
||||
<el-table-column
|
||||
v-if="page && limit && !toggleCustomIndex"
|
||||
prop="_pageIndex"
|
||||
:label="'tableHeader.index' | i18nFilter"
|
||||
width="70"
|
||||
align="center"
|
||||
fixed
|
||||
/>
|
||||
<el-table-column
|
||||
v-for="item in renderTableHeadList"
|
||||
:key="item.prop"
|
||||
v-bind="item"
|
||||
:align="item.align ? item.align : 'left'"
|
||||
:min-width="item.minWidth ? item.minWidth : 150"
|
||||
:fixed="item.isFixed ? true : false"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<component
|
||||
:is="item.subcomponent"
|
||||
v-if="item.subcomponent"
|
||||
:key="scope.row.id"
|
||||
:inject-data="{ ...scope.row, ...item }"
|
||||
@emitData="emitData"
|
||||
/>
|
||||
<span v-else>{{
|
||||
scope.row[item.prop] | commonFilter(item.filter)
|
||||
}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<slot name="content" />
|
||||
<slot name="handleBtn" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "BaseTable",
|
||||
filters: {
|
||||
commonFilter: (source, filterType = (a) => a) => {
|
||||
return filterType(source);
|
||||
},
|
||||
},
|
||||
props: {
|
||||
toggleCustomIndex: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
topBtnConfig: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
tableData: {
|
||||
type: Array,
|
||||
required: true,
|
||||
// validator: (val) => val.filter((item) => !isObject(item)).length === 0,
|
||||
},
|
||||
tableConfig: {
|
||||
type: Array,
|
||||
required: true,
|
||||
// validator: (val) =>
|
||||
// val.filter((item) => !isString(item.prop) || !isString(item.label))
|
||||
// .length === 0,
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
page: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
sbox: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
highIndex: {
|
||||
// 首行是否默认高亮
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableConfigBak: [],
|
||||
selectedBox: new Array(100).fill(true),
|
||||
// tableHeight,
|
||||
tableRowIndex: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
renderData() {
|
||||
return this.tableData.map((item, index) => {
|
||||
return {
|
||||
...item,
|
||||
_pageIndex: (this.page - 1) * this.limit + index + 1,
|
||||
};
|
||||
});
|
||||
},
|
||||
renderTableHeadList() {
|
||||
return this.tableConfig.filter((item, index) => {
|
||||
return this.selectedBox[index];
|
||||
});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
tableData: function (val) {
|
||||
if (this.highIndex) {
|
||||
// 默认高亮行的,重置默认高亮行为第一行
|
||||
this.tableRowIndex = 0;
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeMount() {
|
||||
this.selectedBox = new Array(100).fill(true);
|
||||
if (this.highIndex) {
|
||||
this.tableRowIndex = 0;
|
||||
}
|
||||
},
|
||||
// mounted() {
|
||||
// this.tableConfigBak = cloneDeep(this.tableConfig).map(item => {
|
||||
// return {
|
||||
// ...item,
|
||||
// selected: true
|
||||
// }
|
||||
// })
|
||||
// },
|
||||
methods: {
|
||||
emitData(val) {
|
||||
this.$emit("emitFun", val);
|
||||
},
|
||||
clickTopButton(val) {
|
||||
this.$emit("clickTopBtn", val);
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.$emit("selectChangeFun", val);
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.tableRowIndex = this.getArrayIndex(this.tableData, row); // 获取当前点击行下标
|
||||
this.$emit("selectRow", row);
|
||||
},
|
||||
tableRowClassName({ row, rowIndex }) {
|
||||
if (rowIndex === this.tableRowIndex) {
|
||||
return "success-row";
|
||||
}
|
||||
return "";
|
||||
},
|
||||
getArrayIndex(arr, obj) {
|
||||
var i = arr.length;
|
||||
while (i--) {
|
||||
if (arr[i].id === obj.id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
19
src/components/BaseUpload.vue
Normal file
19
src/components/BaseUpload.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<!-- 基本上传 -->
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: '',
|
||||
props: {},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
433
src/components/DialogWithMenu.vue
Normal file
433
src/components/DialogWithMenu.vue
Normal file
@@ -0,0 +1,433 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
class="dialog-with-menu"
|
||||
:visible="selfVisible"
|
||||
@close="handleClose"
|
||||
:distory-on-close="true"
|
||||
>
|
||||
<!-- title -->
|
||||
<div slot="title" class="">
|
||||
<h1 class="">编辑</h1>
|
||||
</div>
|
||||
<!-- menu -->
|
||||
<el-tabs v-model="activeMenu" type="card" @tab-click="handleTabClick">
|
||||
<!-- <el-tab-pane v-for="(tab, index) in configs.menu" :key="index" :label="tab.name" :name="tab.name"> -->
|
||||
<el-tab-pane
|
||||
v-for="(tab, index) in actualMenus"
|
||||
:key="index"
|
||||
:label="tab.name"
|
||||
:name="tab.name"
|
||||
>
|
||||
<div v-if="index === 0">
|
||||
<!-- form -->
|
||||
<el-form ref="dataForm" :model="dataForm">
|
||||
<el-row
|
||||
v-for="(row, rowIndex) in configs.form.rows"
|
||||
:key="'row_' + rowIndex"
|
||||
:gutter="20"
|
||||
>
|
||||
<el-col
|
||||
v-for="(col, colIndex) in row"
|
||||
:key="colIndex"
|
||||
:span="24 / row.length"
|
||||
>
|
||||
<el-form-item
|
||||
:label="col.label"
|
||||
:prop="col.prop"
|
||||
:rules="col.rules || null"
|
||||
>
|
||||
<el-input
|
||||
v-if="col.input"
|
||||
v-model="dataForm[col.prop]"
|
||||
clearable
|
||||
:disabled="detailMode"
|
||||
v-bind="col.elparams"
|
||||
/>
|
||||
<el-select
|
||||
v-if="col.select"
|
||||
v-model="dataForm[col.prop]"
|
||||
clearable
|
||||
:disabled="detailMode"
|
||||
v-bind="col.elparams"
|
||||
@change="handleSelectChange(col, $event)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(opt, optIdx) in col.options"
|
||||
:key="'option_' + optIdx"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-switch
|
||||
v-if="col.switch"
|
||||
v-model="dataForm[col.prop]"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
@change="handleSwitchChange"
|
||||
:disabled="detailMode"
|
||||
/>
|
||||
<el-input
|
||||
v-if="col.textarea"
|
||||
type="textarea"
|
||||
v-model="dataForm[col.prop]"
|
||||
:disabled="detailMode"
|
||||
v-bind="col.elparams"
|
||||
/>
|
||||
<!-- add more... -->
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<div v-if="dataForm.id && index === 1">
|
||||
<el-button
|
||||
v-if="!detailMode"
|
||||
type="primary"
|
||||
style="margin-bottom: 16px"
|
||||
@click="handleAddParam()"
|
||||
>添加</el-button
|
||||
>
|
||||
<BaseListTable
|
||||
:table-config="null"
|
||||
:column-config="filteredTableProps"
|
||||
:table-data="subList"
|
||||
@operate-event="handleTableRowOperate"
|
||||
/>
|
||||
<!-- paginator -->
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- sub dialog -->
|
||||
<small-dialog
|
||||
:append-to-body="true"
|
||||
v-if="showSubDialog"
|
||||
ref="subDialog"
|
||||
:url="urls.subase"
|
||||
:configs="configs.subDialog"
|
||||
:related-id="dataForm.id"
|
||||
@refreshDataList="getSubList"
|
||||
></small-dialog>
|
||||
|
||||
<!-- footer -->
|
||||
<div slot="footer">
|
||||
<template v-for="(operate, index) in configs.form.operations">
|
||||
<el-button
|
||||
v-if="showButton(operate)"
|
||||
:key="'operation_' + index"
|
||||
:type="operate.type"
|
||||
@click="handleBtnClick(operate)"
|
||||
>{{ operate.label }}</el-button
|
||||
>
|
||||
</template>
|
||||
<el-button @click="handleBtnClick({ name: 'cancel' })">取消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pick as __pick } from "@/utils/filters";
|
||||
import SmallDialog from "@/components/SmallDialog.vue";
|
||||
import BaseListTable from "@/components/BaseListTable.vue";
|
||||
|
||||
export default {
|
||||
name: "DialogWithMenu",
|
||||
components: { SmallDialog, BaseListTable },
|
||||
props: {
|
||||
configs: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
inject: ["urls"],
|
||||
data() {
|
||||
const dataForm = {};
|
||||
this.configs.form.rows.forEach((row) => {
|
||||
row.forEach((col) => {
|
||||
dataForm[col.prop] = col.default ?? "";
|
||||
console.log("==========>", col.prop, dataForm[col.prop]);
|
||||
});
|
||||
});
|
||||
return {
|
||||
// configs,
|
||||
activeMenu: this.configs.menu[0].name,
|
||||
dataForm,
|
||||
detailMode: false,
|
||||
selfVisible: false,
|
||||
showBaseDialog: false,
|
||||
baseDialogConfig: null,
|
||||
subList: [],
|
||||
showSubDialog: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
actualMenus() {
|
||||
return this.configs.menu.filter((m) => {
|
||||
if (m.onlyEditMode && !this.dataForm.id) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
filteredTableProps() {
|
||||
return this.detailMode
|
||||
? this.configs.table.props.filter((v) => v.prop !== "operations")
|
||||
: this.configs.table.props;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/** utitilities */
|
||||
showButton(operate) {
|
||||
const notDetailMode = !this.detailMode;
|
||||
const showAlways = operate.showAlways ?? false;
|
||||
const editMode = operate.showOnEdit && this.dataForm.id;
|
||||
const addMode = !operate.showOnEdit && !this.dataForm.id;
|
||||
const permission = operate.permission
|
||||
? this.$hasPermission(operate.permission)
|
||||
: true;
|
||||
return (
|
||||
notDetailMode && (showAlways || ((editMode || addMode) && permission))
|
||||
);
|
||||
},
|
||||
resetForm(excludeId = false) {
|
||||
setTimeout(() => {
|
||||
Object.keys(this.dataForm).forEach((key) => {
|
||||
if (excludeId && key === "id") return;
|
||||
this.dataForm[key] = null;
|
||||
});
|
||||
this.activeMenu = this.configs.menu[0].name;
|
||||
}, 500);
|
||||
},
|
||||
|
||||
/** init **/
|
||||
init(id, detailMode) {
|
||||
console.log("[dialog] DialogWithHead init():", id, detailMode);
|
||||
|
||||
this.detailMode = detailMode ?? false;
|
||||
this.$nextTick(() => {
|
||||
// this.$refs['dataForm'].resetFields();
|
||||
|
||||
this.dataForm.id = id || null;
|
||||
if (this.dataForm.id) {
|
||||
// 如果是编辑
|
||||
this.$http
|
||||
.get(this.urls.base + `/${this.dataForm.id}`)
|
||||
.then(({ data: res }) => {
|
||||
// dev env:
|
||||
// if (LOCAL) res.data.id = res.data._id;
|
||||
// end dev env
|
||||
console.log("[base dialog init] ", res);
|
||||
if (res && res.code === 0) {
|
||||
const dataFormKeys = Object.keys(this.dataForm);
|
||||
console.log("keys ===> ", dataFormKeys);
|
||||
// console.log('data form keys: ', dataFormKeys, pick(res.data, dataFormKeys))
|
||||
this.dataForm = __pick(res.data, dataFormKeys);
|
||||
console.log(
|
||||
"pick(res.data, dataFormKeys) ===> ",
|
||||
__pick(res.data, dataFormKeys)
|
||||
);
|
||||
// LABEL: FILE_RELATED
|
||||
/** 对文件下载进行分流 */
|
||||
// this.fileList = {};
|
||||
// if (this.dataForm.files) {
|
||||
// // console.log('files: ', this.dataForm.files)
|
||||
// this.dataForm.files.forEach((file) => {
|
||||
// // const fileName = file.fileurls.split('/').pop()
|
||||
// /** [1] 处理 fileList */
|
||||
// // if (Object.hasOwn(this.fileList, file.typeCode)) {
|
||||
// if (this.fileList.hasOwnProperty(file.typeCode)) {
|
||||
// /** 已存在 */
|
||||
// // this.fileList[file.typeCode].push({ id: file.id, name: fileName, typeCode: file.typeCode })
|
||||
// this.fileList[file.typeCode].push(file);
|
||||
// } else {
|
||||
// // this.fileList[file.typeCode] = [{ id: file.id, name: fileName, typeCode: file.typeCode }]
|
||||
// this.fileList[file.typeCode] = [file];
|
||||
// }
|
||||
|
||||
// /** [2] 处理 fileForm */
|
||||
// // if (Object.hasOwn(this.fileForm, file.typeCode)) {
|
||||
// if (this.fileForm.hasOwnProperty(file.typeCode)) {
|
||||
// this.fileForm[file.typeCode].push(file.id);
|
||||
// } else {
|
||||
// this.fileForm[file.typeCode] = [file.id];
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 如果不是编辑
|
||||
}
|
||||
});
|
||||
|
||||
this.selfVisible = true;
|
||||
},
|
||||
|
||||
/** handlers */
|
||||
handleSelectChange(col, eventValue) {
|
||||
console.log("[dialog] select change: ", col, eventValue);
|
||||
},
|
||||
handleSwitchChange(val) {
|
||||
console.log("[dialog] switch change: ", val, this.dataForm);
|
||||
},
|
||||
handleBtnClick(payload) {
|
||||
console.log("btn click payload: ", payload);
|
||||
|
||||
if ("name" in payload) {
|
||||
switch (payload.name) {
|
||||
case "cancel":
|
||||
this.handleClose();
|
||||
break;
|
||||
case "reset":
|
||||
this.resetForm(true); // true means exclude id
|
||||
break;
|
||||
case "add":
|
||||
case "update": {
|
||||
const method = payload.name === "add" ? "POST" : "PUT";
|
||||
this.$http({
|
||||
url: this.urls.base,
|
||||
method,
|
||||
data: this.dataForm,
|
||||
}).then(({ data: res }) => {
|
||||
console.log("[add&update] res is: ", res);
|
||||
this.$message.success(
|
||||
payload.name === "add" ? "添加成功" : "更新成功"
|
||||
);
|
||||
this.$emit("refreshDataList");
|
||||
this.handleClose();
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("[x] 不是这么用的! 缺少name属性");
|
||||
}
|
||||
},
|
||||
handleTabClick(payload) {
|
||||
console.log("tab click payload: ", this.activeMenu);
|
||||
if (this.activeMenu === this.configs.menu[1].name) {
|
||||
// 获取数据
|
||||
this.getSubList();
|
||||
}
|
||||
},
|
||||
|
||||
getSubList(page = 1, size = 20) {
|
||||
const params = {};
|
||||
params.page = page;
|
||||
params.limit = size;
|
||||
|
||||
const requiredParams = this.configs.table.extraParams;
|
||||
if (requiredParams) {
|
||||
if (Array.isArray(requiredParams)) {
|
||||
requiredParams.forEach((str) => {
|
||||
if (/id/i.test(str)) {
|
||||
params[str] = this.dataForm.id;
|
||||
} else {
|
||||
params[str] = "";
|
||||
}
|
||||
});
|
||||
} else if (typeof requiredParams === "string") {
|
||||
// 如果需要额外参数,一般肯定需要
|
||||
params[this.configs.table.extraParams] = this.dataForm.id;
|
||||
// 此时 dataForm.id 一定是存在的
|
||||
}
|
||||
}
|
||||
this.$http.get(this.urls.subpage, { params }).then(({ data: res }) => {
|
||||
console.log("[subList] getSubList:", res);
|
||||
if (res.code === 0 && res.data?.list) {
|
||||
// 有数据
|
||||
this.subList = res.data.list;
|
||||
} else {
|
||||
this.subList.splice(0);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleAddParam(id) {
|
||||
this.showSubDialog = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.subDialog.init(id ?? null);
|
||||
});
|
||||
},
|
||||
|
||||
handleClose() {
|
||||
this.resetForm();
|
||||
this.selfVisible = false;
|
||||
},
|
||||
|
||||
/** 列表handlers */
|
||||
handleAddItem() {
|
||||
// console.log('[dialog] handleAddItem ot list...');
|
||||
this.showBaseDialog = true;
|
||||
this.$nextTick(() => {
|
||||
console.log("[sub-dialog] ", this.$refs["sub-dialog"].init());
|
||||
});
|
||||
},
|
||||
handleTableRowOperate({ type, data }) {
|
||||
console.log("handleTableRowOperate", type, data);
|
||||
|
||||
switch (type) {
|
||||
case "delete": {
|
||||
// 确认是否删除
|
||||
return this.$confirm(`是否删除条目: ${data}`, "提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "我再想想",
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
// this.$http.delete(this.urls.base + `/${data}`).then((res) => {
|
||||
this.$http({
|
||||
url: this.urls.subase,
|
||||
method: "DELETE",
|
||||
data: [`${data}`],
|
||||
}).then(({ data: res }) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success({
|
||||
message: "删除成功!",
|
||||
duration: 1000,
|
||||
onClose: () => {
|
||||
this.getSubList(1, 20);
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((err) => {});
|
||||
}
|
||||
case "edit": {
|
||||
this.handleAddParam(data); /** data is ==> id */
|
||||
break;
|
||||
}
|
||||
// case 'view-detail-action':
|
||||
// this.openDialog(data, true);
|
||||
// break;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-menu {
|
||||
margin: 16px 0 !important;
|
||||
}
|
||||
|
||||
.el-menu.el-menu--horizontal {
|
||||
border: none !important;
|
||||
/* background: #0f02 !important; */
|
||||
}
|
||||
|
||||
/* .el-menu--horizontal > .el-menu-item.is-active { */
|
||||
/* border-bottom-color: #0b58ff; */
|
||||
/* } */
|
||||
|
||||
.dialog-with-menu >>> .el-dialog__body {
|
||||
/* padding-top: 16px !important;
|
||||
padding-bottom: 16px !important; */
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.el-select {
|
||||
width: 100% !important;
|
||||
}
|
||||
</style>
|
||||
2
src/components/README.config.md
Normal file
2
src/components/README.config.md
Normal file
@@ -0,0 +1,2 @@
|
||||
配置文件选项总结
|
||||
|
||||
220
src/components/SmallDialog.vue
Normal file
220
src/components/SmallDialog.vue
Normal file
@@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<!-- :title="isDetail ? title.detail : !dataForm.id ? title.add : title.edit" -->
|
||||
<el-dialog
|
||||
class="a-small-dialog"
|
||||
:visible.sync="visible"
|
||||
@close="handleClose"
|
||||
:distory-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<!-- :append-to-body="appendToBody"> -->
|
||||
<div>
|
||||
<el-form ref="dataForm" :model="dataForm">
|
||||
<el-row
|
||||
v-for="(row, rowIndex) in configs.rows"
|
||||
:key="'row_' + rowIndex"
|
||||
:gutter="20"
|
||||
>
|
||||
<el-col
|
||||
v-for="(col, colIndex) in row"
|
||||
:key="colIndex"
|
||||
:span="col.span ?? 24 / row.length"
|
||||
>
|
||||
<el-form-item
|
||||
:prop="col.prop"
|
||||
:rules="col.rules || null"
|
||||
:label="col.label"
|
||||
>
|
||||
<el-input
|
||||
v-if="col.input"
|
||||
v-model="dataForm[col.prop]"
|
||||
clearable
|
||||
:disabled="detailMode"
|
||||
v-bind="col.elparams"
|
||||
/>
|
||||
<el-select
|
||||
v-if="col.select"
|
||||
v-model="dataForm[col.prop]"
|
||||
clearable
|
||||
:disabled="detailMode"
|
||||
v-bind="col.elparams"
|
||||
@change="handleSelectChange(col, $event)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(opt, optIdx) in col.options"
|
||||
:key="'option_' + optIdx"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-switch
|
||||
v-if="col.switch"
|
||||
v-model="dataForm[col.prop]"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
@change="handleSwitchChange"
|
||||
:disabled="detailMode"
|
||||
/>
|
||||
<el-input
|
||||
v-if="col.textarea"
|
||||
type="textarea"
|
||||
v-model="dataForm[col.prop]"
|
||||
:disabled="detailMode"
|
||||
v-bind="col.elparams"
|
||||
/>
|
||||
<!-- add more... -->
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- footer -->
|
||||
<div slot="footer">
|
||||
<template v-for="(operate, index) in configs.operations">
|
||||
<el-button
|
||||
v-if="showButton(operate)"
|
||||
:key="'operation_' + index"
|
||||
:type="operate.type"
|
||||
@click="handleBtnClick(operate)"
|
||||
>{{ operate.label }}</el-button
|
||||
>
|
||||
</template>
|
||||
<el-button @click="handleBtnClick({ name: 'cancel' })">取消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import CKEditor from 'ckeditor4-vue'
|
||||
// import AttrForm from './AttrForm'
|
||||
// import { pick } from 'lodash/object'
|
||||
import { pick as __pick } from "@/utils/filters";
|
||||
// import i18n from '@/i18n'
|
||||
|
||||
export default {
|
||||
name: "SmallDialog",
|
||||
props: {
|
||||
configs: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
relatedId: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
inject: ["urls"],
|
||||
data() {
|
||||
const dataForm = {};
|
||||
this.configs.rows.forEach((row) => {
|
||||
row.forEach((col) => {
|
||||
dataForm[col.prop] = col.default ?? "";
|
||||
console.log("[small dialog]==========>", col.prop, dataForm[col.prop]);
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
visible: false,
|
||||
detailMode: false,
|
||||
dataForm,
|
||||
dataFormRules: {},
|
||||
tempForm: [], // 临时保存自动生成的code,或其他数据
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/** utitilities */
|
||||
showButton(operate) {
|
||||
const notDetailMode = !this.detailMode;
|
||||
const showAlways = operate.showAlways ?? false;
|
||||
const editMode = operate.showOnEdit && this.dataForm.id;
|
||||
const addMode = !operate.showOnEdit && !this.dataForm.id;
|
||||
const permission = operate.permission
|
||||
? this.$hasPermission(operate.permission)
|
||||
: true;
|
||||
return (
|
||||
notDetailMode && (showAlways || ((editMode || addMode) && permission))
|
||||
);
|
||||
},
|
||||
resetForm(excludeId = false) {
|
||||
setTimeout(() => {
|
||||
Object.keys(this.dataForm).forEach((key) => {
|
||||
console.log(">>> clearing key: ", key);
|
||||
if (excludeId && key === "id") return;
|
||||
this.dataForm[key] = null;
|
||||
});
|
||||
this.detailMode = false;
|
||||
}, 500);
|
||||
},
|
||||
|
||||
init(id, isdetail = false) {
|
||||
this.detailMode = isdetail;
|
||||
console.log("[small dialog] init", id, isdetail);
|
||||
|
||||
this.$nextTick(() => {
|
||||
// this.$refs['dataForm'].resetFields();
|
||||
this.dataForm.id = id || null;
|
||||
|
||||
if (this.dataForm.id) {
|
||||
// 如果是编辑
|
||||
$http.get(this.urls.subase + `/${this.dataForm.id}`).then((res) => {
|
||||
// dev env:
|
||||
if (LOCAL) res.data.id = res.data._id;
|
||||
// end dev env
|
||||
if (res && res.code === 0) {
|
||||
const dataFormKeys = Object.keys(this.dataForm);
|
||||
this.dataForm = __pick(res.data, dataFormKeys);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 如果不是编辑
|
||||
}
|
||||
});
|
||||
|
||||
this.visible = true;
|
||||
},
|
||||
|
||||
handleSelectChange(col, event) {},
|
||||
handleSwitchChange() {},
|
||||
|
||||
handleBtnClick(payload) {
|
||||
console.log("btn click payload: ", payload);
|
||||
console.log("configs", this.configs);
|
||||
if ("name" in payload) {
|
||||
switch (payload.name) {
|
||||
case "cancel":
|
||||
this.handleClose();
|
||||
break;
|
||||
case "add":
|
||||
case "update": {
|
||||
const method = payload.name === "add" ? "POST" : "PUT";
|
||||
$http({
|
||||
url: this.urls.subase,
|
||||
method,
|
||||
data: {
|
||||
...this.dataForm,
|
||||
[this.configs.extraParams]: this.relatedId,
|
||||
},
|
||||
}).then((res) => {
|
||||
console.log("[add&update] res is: ", res);
|
||||
this.$message.success(
|
||||
payload.name === "add" ? "添加成功" : "更新成功"
|
||||
);
|
||||
this.$emit("refreshDataList");
|
||||
this.handleClose();
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("[x] 不是这么用的! 缺少name属性");
|
||||
}
|
||||
},
|
||||
|
||||
handleClose() {
|
||||
this.resetForm();
|
||||
this.visible = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
40
src/components/TableHead.vue
Normal file
40
src/components/TableHead.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<el-table-column
|
||||
:label="opt.label ? opt.label : opt.name"
|
||||
:prop="opt.prop || null"
|
||||
:width="opt.width || null"
|
||||
:min-width="opt.minWidth || null"
|
||||
:fixed="opt.fixed || null"
|
||||
:show-overflow-tooltip="opt.showOverflowTooltip || false"
|
||||
filter-placement="top"
|
||||
:align="opt.align || null"
|
||||
v-bind="opt.more"
|
||||
>
|
||||
<template v-if="opt.prop" slot-scope="scope">
|
||||
<component v-if="opt.subcomponent" :is="opt.subcomponent" :key="idx + 'sub'" :inject-data="{ ...scope.row, head: opt }" @emit-data="handleSubEmitData" />
|
||||
<!-- 直接展示数据或应用过滤器 -->
|
||||
<span v-else>{{ scope.row[opt.prop] | commonFilter(opt.filter) }}</span>
|
||||
</template>
|
||||
<!-- 递归 -->
|
||||
<template v-if="!opt.prop && opt.children">
|
||||
<TableHead v-for="(subhead, index) in opt.children" :key="'subhead' + index" :opt="subhead" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TableHead',
|
||||
filters: {
|
||||
commonFilter: (source, filterType = a => a) => {
|
||||
return filterType(source)
|
||||
}
|
||||
},
|
||||
props: {
|
||||
opt: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
1
src/components/noTemplateComponents/README.md
Normal file
1
src/components/noTemplateComponents/README.md
Normal file
@@ -0,0 +1 @@
|
||||
用于存放没有模板的组件,一般用于表格中或弹窗里的组件插入
|
||||
31
src/components/noTemplateComponents/detailComponent.js
Normal file
31
src/components/noTemplateComponents/detailComponent.js
Normal file
@@ -0,0 +1,31 @@
|
||||
// import i18n from '@/i18n'
|
||||
|
||||
export default {
|
||||
name: 'TableTextComponent',
|
||||
props: {
|
||||
injectData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// for i18n inject:
|
||||
// defaultText: i18n.t('viewdetail')
|
||||
defaultText: '查看详情'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
emitClick() {
|
||||
// console.log('inject data:' ,this.injectData)
|
||||
this.$emit('emit-data', {
|
||||
type: this.injectData.head?.actionName || 'view-detail-action',
|
||||
data: this.injectData.head?.emitFullData ? this.injectData : this.injectData.id
|
||||
})
|
||||
}
|
||||
},
|
||||
render: function (h) {
|
||||
// console.log('button content:', this.injectData)
|
||||
return h('span', null, [h('el-button', { props: { type: 'text' }, style: { paddingLeft: 0 }, on: { click: this.emitClick } }, this.injectData.head?.buttonContent || this.defaultText)])
|
||||
}
|
||||
}
|
||||
98
src/components/noTemplateComponents/operationComponent.js
Normal file
98
src/components/noTemplateComponents/operationComponent.js
Normal file
@@ -0,0 +1,98 @@
|
||||
// import i18n from '@/i18n'
|
||||
|
||||
export default {
|
||||
name: 'TableOperations',
|
||||
props: {
|
||||
injectData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
btnTypes: {
|
||||
add: 'primary',
|
||||
delete: 'danger',
|
||||
detail: 'info'
|
||||
// add more...
|
||||
},
|
||||
colors: {
|
||||
delete: '#FF5454',
|
||||
preview: '#f09843',
|
||||
design: '#99089f',
|
||||
// 'view-trend': 'red'
|
||||
// add more...
|
||||
},
|
||||
text: {
|
||||
// TODO: i18n
|
||||
// edit: i18n.t('edit'),
|
||||
// detail: i18n.t('detail'),
|
||||
// delete: i18n.t('delete'),
|
||||
// viewAttr: i18n.t('viewattr'),
|
||||
// preview: i18n.t('preview'),
|
||||
// design: i18n.t('design'),
|
||||
edit: '编辑',
|
||||
detail: '详情',
|
||||
delete: '删除',
|
||||
viewAttr: '查看属性',
|
||||
preview: '预览',
|
||||
design: '设计',
|
||||
'view-trend': '查看趋势'
|
||||
// add more...
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 发射事件
|
||||
emit(opt) {
|
||||
let emitFull = false
|
||||
let eventType = 'default'
|
||||
let customField
|
||||
if (typeof opt === 'object') {
|
||||
eventType = opt.name
|
||||
customField = opt.emitField || 'id'
|
||||
emitFull = opt.emitFull || false
|
||||
} else {
|
||||
eventType = opt
|
||||
}
|
||||
this.$emit('emit-data', { type: eventType, data: emitFull ? this.injectData : customField ? this.injectData[customField] : this.injectData.id })
|
||||
}
|
||||
},
|
||||
render: function (h) {
|
||||
let btns = []
|
||||
for (const opt of this.injectData.head?.options) {
|
||||
const optIsObj = typeof opt === 'object'
|
||||
|
||||
if (optIsObj) {
|
||||
// 可能需要验证权限,如 opt.permission 选项
|
||||
// 注意:为空字符串或null/undefined都会不验证权限
|
||||
if (!opt.permission || (opt.permission && this.$hasPermission(opt.permission))) {
|
||||
btns.push(
|
||||
h('el-button',
|
||||
{
|
||||
props: { type: 'text' },
|
||||
style: { color: this.colors[optionStr.name] || '#409EFF' },
|
||||
on: { click: this.emit.bind(null, opt) }
|
||||
},
|
||||
this.text[opt.name]
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// 此时 opt 是一个 string,且默认有操作权限
|
||||
btns.push(
|
||||
h('el-button',
|
||||
{
|
||||
props: { type: 'text' },
|
||||
style: { color: this.colors[opt] || '#409EFF' },
|
||||
on: { click: this.emit.bind(null, opt) }
|
||||
},
|
||||
this.text[opt]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
return h('span', null, btns)
|
||||
}
|
||||
}
|
||||
2
src/components/noTemplateComponents/richInput.js
Normal file
2
src/components/noTemplateComponents/richInput.js
Normal file
@@ -0,0 +1,2 @@
|
||||
// 富文本组件
|
||||
export default {}
|
||||
47
src/components/noTemplateComponents/statusComponent.js
Normal file
47
src/components/noTemplateComponents/statusComponent.js
Normal file
@@ -0,0 +1,47 @@
|
||||
// import i18n from '@/i18n'
|
||||
|
||||
export default {
|
||||
name: 'StatusComponent',
|
||||
props: {
|
||||
injectData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statusText: [
|
||||
'正常',
|
||||
'异常',
|
||||
'损坏',
|
||||
// more...
|
||||
],
|
||||
statusType: [
|
||||
'success',
|
||||
'warning',
|
||||
'danger',
|
||||
// more...
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isEnabled() {
|
||||
return this.injectData && (this.injectData.enabled === 1 || this.injectData.enabled.toString() === '1')
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log('[component] StatusComponent: ', this.injectData)
|
||||
},
|
||||
methods: {
|
||||
// 发射事件
|
||||
emit(opt) { }
|
||||
},
|
||||
render: function (h) {
|
||||
return h('el-tag',
|
||||
{
|
||||
props:
|
||||
{ type: this.isEnabled ? this.statusType[0] : this.statusType[1] }
|
||||
},
|
||||
this.isEnabled ? this.statusText[0] : this.statusText[1])
|
||||
}
|
||||
}
|
||||
2
src/components/noTemplateComponents/switchBtn.js
Normal file
2
src/components/noTemplateComponents/switchBtn.js
Normal file
@@ -0,0 +1,2 @@
|
||||
// 表格中的开关
|
||||
export default {}
|
||||
2
src/components/noTemplateComponents/textBtn.js
Normal file
2
src/components/noTemplateComponents/textBtn.js
Normal file
@@ -0,0 +1,2 @@
|
||||
// 表格中的可点击文本
|
||||
export default {}
|
||||
7
src/components/ren-dept-tree/index.js
Normal file
7
src/components/ren-dept-tree/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import RenDeptTree from './src/ren-dept-tree'
|
||||
|
||||
RenDeptTree.install = function (Vue) {
|
||||
Vue.component(RenDeptTree.name, RenDeptTree)
|
||||
}
|
||||
|
||||
export default RenDeptTree
|
||||
118
src/components/ren-dept-tree/src/ren-dept-tree.vue
Normal file
118
src/components/ren-dept-tree/src/ren-dept-tree.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-input v-model="showDeptName" :placeholder="placeholder" @focus="deptDialog">
|
||||
<el-button slot="append" icon="el-icon-search" @click="deptDialog"></el-button>
|
||||
</el-input>
|
||||
<el-input :value="value" style="display: none"></el-input>
|
||||
<el-dialog :visible.sync="visibleDept" width="30%" :modal="false" :title="placeholder" :close-on-click-modal="false" :close-on-press-escape="false">
|
||||
<el-form size="mini" :inline="true">
|
||||
<el-form-item :label="$t('keyword')">
|
||||
<el-input v-model="filterText"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="default">{{ $t('query') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-tree
|
||||
class="filter-tree"
|
||||
:data="deptList"
|
||||
:default-expanded-keys="expandedKeys"
|
||||
:props="{ label: 'name', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
:highlight-current="true"
|
||||
node-key="id"
|
||||
ref="tree">
|
||||
</el-tree>
|
||||
<template slot="footer">
|
||||
<el-button type="default" @click="cancelHandle()" size="mini">{{ $t('cancel') }}</el-button>
|
||||
<el-button v-if="query" type="info" @click="clearHandle()" size="mini">{{ $t('clear') }}</el-button>
|
||||
<el-button type="primary" @click="commitHandle()" size="mini">{{ $t('confirm') }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'RenDeptTree',
|
||||
data () {
|
||||
return {
|
||||
filterText: '',
|
||||
visibleDept: false,
|
||||
deptList: [],
|
||||
showDeptName: '',
|
||||
expandedKeys: null,
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'label'
|
||||
}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: [Number, String],
|
||||
deptName: String,
|
||||
query: Boolean,
|
||||
placeholder: String
|
||||
},
|
||||
watch: {
|
||||
filterText (val) {
|
||||
this.$refs.tree.filter(val)
|
||||
},
|
||||
deptName (val) {
|
||||
this.showDeptName = val
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deptDialog () {
|
||||
this.expandedKeys = null
|
||||
if (this.$refs.tree) {
|
||||
this.$refs.tree.setCurrentKey(null)
|
||||
}
|
||||
this.visibleDept = true
|
||||
this.getDeptList(this.value)
|
||||
},
|
||||
filterNode (value, data) {
|
||||
if (!value) return true
|
||||
return data.name.indexOf(value) !== -1
|
||||
},
|
||||
getDeptList (id) {
|
||||
return this.$http.get('/sys/dept/list').then(({ data: res }) => {
|
||||
if (res.code !== 0) {
|
||||
return this.$message.error(res.msg)
|
||||
}
|
||||
this.deptList = res.data
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tree.setCurrentKey(id)
|
||||
this.expandedKeys = [id]
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
cancelHandle () {
|
||||
this.visibleDept = false
|
||||
this.deptList = []
|
||||
this.filterText = ''
|
||||
},
|
||||
clearHandle () {
|
||||
this.$emit('input', '')
|
||||
this.$emit('update:deptName', '')
|
||||
this.showDeptName = ''
|
||||
this.visibleDept = false
|
||||
this.deptList = []
|
||||
this.filterText = ''
|
||||
},
|
||||
commitHandle () {
|
||||
const node = this.$refs.tree.getCurrentNode()
|
||||
if (!node) {
|
||||
this.$message.error(this.$t('dept.chooseerror'))
|
||||
return
|
||||
}
|
||||
this.$emit('input', node.id)
|
||||
this.$emit('update:deptName', node.name)
|
||||
this.showDeptName = node.name
|
||||
this.visibleDept = false
|
||||
this.deptList = []
|
||||
this.filterText = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
7
src/components/ren-radio-group/index.js
Normal file
7
src/components/ren-radio-group/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import RenRadioGroup from './src/ren-radio-group'
|
||||
|
||||
RenRadioGroup.install = function (Vue) {
|
||||
Vue.component(RenRadioGroup.name, RenRadioGroup)
|
||||
}
|
||||
|
||||
export default RenRadioGroup
|
||||
20
src/components/ren-radio-group/src/ren-radio-group.vue
Normal file
20
src/components/ren-radio-group/src/ren-radio-group.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<el-radio-group :value="value+''" @input="$emit('input', $event)">
|
||||
<el-radio :label="data.dictValue" v-for="data in dataList" :key="data.dictValue">{{data.dictLabel}}</el-radio>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<script>
|
||||
import { getDictDataList } from '@/utils'
|
||||
export default {
|
||||
name: 'RenRadioGroup',
|
||||
data () {
|
||||
return {
|
||||
dataList: getDictDataList(this.dictType)
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: [Number, String],
|
||||
dictType: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
7
src/components/ren-region-tree/index.js
Normal file
7
src/components/ren-region-tree/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import RenRegionTree from './src/ren-region-tree'
|
||||
|
||||
RenRegionTree.install = function (Vue) {
|
||||
Vue.component(RenRegionTree.name, RenRegionTree)
|
||||
}
|
||||
|
||||
export default RenRegionTree
|
||||
132
src/components/ren-region-tree/src/ren-region-tree.vue
Normal file
132
src/components/ren-region-tree/src/ren-region-tree.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div class="ren-region">
|
||||
<el-input v-model="showName" :placeholder="placeholder" @focus="treeDialog">
|
||||
<el-button slot="append" icon="el-icon-search" @click="treeDialog"></el-button>
|
||||
</el-input>
|
||||
<el-input :value="value" style="display: none"></el-input>
|
||||
<el-dialog :visible.sync="visibleTree" width="360px" :modal="false" :title="placeholder" :close-on-click-modal="false" :close-on-press-escape="false">
|
||||
<el-form size="mini" :inline="true">
|
||||
<el-form-item :label="$t('keyword')">
|
||||
<el-input v-model="filterText"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="default">{{ $t('query') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-tree
|
||||
class="filter-tree"
|
||||
:data="dataList"
|
||||
:default-expanded-keys="expandedKeys"
|
||||
:props="{ label: 'name', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
:highlight-current="true"
|
||||
node-key="id"
|
||||
ref="tree">
|
||||
</el-tree>
|
||||
<template slot="footer">
|
||||
<el-button type="default" @click="cancelHandle()" size="mini">{{ $t('cancel') }}</el-button>
|
||||
<el-button type="info" @click="clearHandle()" size="mini">{{ $t('clear') }}</el-button>
|
||||
<el-button type="primary" @click="commitHandle()" size="mini">{{ $t('confirm') }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
.ren-region {
|
||||
.filter-tree {
|
||||
max-height: 230px;
|
||||
overflow: auto;
|
||||
}
|
||||
.el-dialog__body {
|
||||
padding: 0px 0px 0px 20px;
|
||||
}
|
||||
.el-dialog__footer {
|
||||
padding: 10px 20px 8px 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import { treeDataTranslate } from '@/utils'
|
||||
export default {
|
||||
name: 'RenRegionTree',
|
||||
data () {
|
||||
return {
|
||||
filterText: '',
|
||||
visibleTree: false,
|
||||
dataList: [],
|
||||
showName: '',
|
||||
expandedKeys: null,
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: [Number, String],
|
||||
parentName: String,
|
||||
placeholder: String
|
||||
},
|
||||
watch: {
|
||||
filterText (val) {
|
||||
this.$refs.tree.filter(val)
|
||||
},
|
||||
parentName (val) {
|
||||
this.showName = val
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
treeDialog () {
|
||||
this.expandedKeys = null
|
||||
if (this.$refs.tree) {
|
||||
this.$refs.tree.setCurrentKey(null)
|
||||
}
|
||||
this.visibleTree = true
|
||||
this.getDataList(this.value)
|
||||
},
|
||||
filterNode (value, data) {
|
||||
if (!value) return true
|
||||
return data.name.indexOf(value) !== -1
|
||||
},
|
||||
getDataList (id) {
|
||||
return this.$http.get('/sys/region/tree').then(({ data: res }) => {
|
||||
if (res.code !== 0) {
|
||||
return this.$message.error(res.msg)
|
||||
}
|
||||
this.dataList = treeDataTranslate(res.data)
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tree.setCurrentKey(id)
|
||||
this.expandedKeys = [id]
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
cancelHandle () {
|
||||
this.visibleTree = false
|
||||
this.dataList = []
|
||||
this.filterText = ''
|
||||
},
|
||||
clearHandle () {
|
||||
this.$emit('input', '0')
|
||||
this.$emit('update:parentName', '')
|
||||
this.showName = ''
|
||||
this.visibleTree = false
|
||||
this.dataList = []
|
||||
this.filterText = ''
|
||||
},
|
||||
commitHandle () {
|
||||
const node = this.$refs.tree.getCurrentNode()
|
||||
if (!node) {
|
||||
this.$message.error(this.$t('choose'))
|
||||
return
|
||||
}
|
||||
this.$emit('input', node.id)
|
||||
this.$emit('update:parentName', node.name)
|
||||
this.showName = node.name
|
||||
this.visibleTree = false
|
||||
this.dataList = []
|
||||
this.filterText = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
7
src/components/ren-select/index.js
Normal file
7
src/components/ren-select/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import RenSelect from './src/ren-select'
|
||||
|
||||
RenSelect.install = function (Vue) {
|
||||
Vue.component(RenSelect.name, RenSelect)
|
||||
}
|
||||
|
||||
export default RenSelect
|
||||
21
src/components/ren-select/src/ren-select.vue
Normal file
21
src/components/ren-select/src/ren-select.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<el-select :value="value+''" @input="$emit('input', $event)" :placeholder="placeholder" clearable>
|
||||
<el-option :label="data.dictLabel" v-for="data in dataList" :key="data.dictValue" :value ="data.dictValue">{{data.dictLabel}}</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
<script>
|
||||
import { getDictDataList } from '@/utils'
|
||||
export default {
|
||||
name: 'RenSelect',
|
||||
data () {
|
||||
return {
|
||||
dataList: getDictDataList(this.dictType)
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: [Number, String],
|
||||
dictType: String,
|
||||
placeholder: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user