master_merge
monkey 3 years ago
parent f131de1dc4
commit 5e7590b474

@ -8,7 +8,7 @@
公司先后获得河南省高新技术企业、国家3A信用企业、IOS9001软件产品认证等20多项荣誉奖项。拥有50余项软件著作权。 获得20余家国内媒体报道。公司自成立以来以高科技为起点以技术为核心、 以完善的售后服务为后盾,秉承稳固与发展、求实与创新的精神,已为国内外上万家企业提供了服务。 在为实现企业价值最大化的过程中, 实现了自身的价值的提升,取得了最大程度的双赢合作,并获得了社会各界的广泛赞誉和认同。
官网地址:[http://www.5kcrm.com](http://www.5kcrm.com/)
官网地址:[http://www.5kcrm.com](http://www.5kcrm.com/)
演示地址:(http://demo11.5kcrm.net/)
帐号18688888888 密码123456a

@ -24,20 +24,21 @@
"axios": "0.18.0",
"babel-polyfill": "^6.26.0",
"clipboard": "^2.0.4",
"echarts": "4.3.0",
"echarts": "^4.3.0",
"el-bigdata-table": "^1.0.32",
"element-ui": "^2.12.0",
"file-saver": "^2.0.1",
"id-validator": "^1.3.0",
"js-cookie": "2.2.0",
"js-md5": "^0.7.3",
"js-cookie": "2.2.1",
"lockr": "^0.8.5",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"number-precision": "^1.5.0",
"numeral": "^2.0.6",
"nzh": "^1.0.4",
"pinyin-match": "1.0.9",
"qrcodejs2": "0.0.2",
"signature_pad": "3.0.0-beta.4",
"throttle-debounce": "^2.1.0",
"vue": "2.5.17",
"vue-bus": "^1.1.0",
@ -48,7 +49,8 @@
"vue-radial-progress": "^0.2.10",
"vue-router": "3.0.1",
"vue2-animate": "^2.1.2",
"vuedraggable": "^2.16.0",
"vue2-org-tree": "1.3.1",
"vuedraggable": "2.24.3",
"vuex": "3.0.1",
"xlsx": "^0.14.1",
"xss": "^1.0.6"
@ -75,6 +77,8 @@
"file-loader": "1.1.11",
"friendly-errors-webpack-plugin": "1.7.0",
"html-webpack-plugin": "4.0.0-alpha",
"less": "3.0.0",
"less-loader": "4.1.0",
"mini-css-extract-plugin": "0.4.1",
"node-notifier": "5.2.1",
"node-sass": "^4.7.2",

@ -78,7 +78,10 @@ export function customFieldHandleAPI(data) {
return request({
url: 'admin/field/update',
method: 'post',
data: data
data: data,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}

@ -52,10 +52,19 @@ export function userAddAPI(params) {
})
}
// // 角色列表
// export function roleListAPI(data) {
// return request({
// url: 'admin/groups/index',
// method: 'post',
// data: data
// })
// }
// 角色列表
export function roleListAPI(data) {
return request({
url: 'admin/groups/index',
url: 'admin/rules/getgroupauth',
method: 'post',
data: data
})
@ -195,3 +204,41 @@ export function adminUserSetUserDeptPI(data) {
}
})
}
/**
* 查询配置的角色范围
* @param {*} data
* @returns
*/
export function adminRoleQueryAuthRoleAPI(data) {
return request({
url: `admin/rules/groupauthId`,
method: 'post',
data
})
}
/**
* 更新配置的角色范围
* @param {*} data
* @returns
*/
export function adminRoleUpdateAuthRoleAPI(data) {
return request({
url: `admin/rules/upgroupauth`,
method: 'post',
data: data,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
// 部分角色列表
export function adminRoleGetRoleListAPI(data) {
return request({
url: 'admin/rules/groupauth',
method: 'post',
data: data
})
}

@ -361,3 +361,25 @@ export function readUpdateNoticeAPI(data) {
data: data
})
}
/**
* 公共web文件上传
* @param data
*/
export function crmFileSingleSaveAPI(data) {
var param = new FormData()
Object.keys(data).forEach(key => {
param.append(key, data[key])
// param.append('isPublic', '1')
// param.append('module', 'print')
param.append('type', 'img')
})
return request({
url: 'admin/file/save',
method: 'post',
data: param,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}

@ -9,7 +9,10 @@ export function crmCustomerSaveAPI(data) {
return request({
url: 'crm/customer/' + url,
method: 'post',
data: data
data: data,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}

@ -188,3 +188,16 @@ export function crmInvoiceDeleteInvoiceInfoAPI(data) {
data: data
})
}
/**
* 发票全部导出
* @param {*} data
*/
export function crmInvoiceExcelAllExportAPI(data) {
return request({
url: 'crm/invoice/excelExport',
method: 'post',
data: data,
responseType: 'blob'
})
}

@ -108,7 +108,7 @@ export function crmIndexFunnelAPI(data) {
*/
export function crmInstrumentSellFunnelBusinessListAPI(data) {
return request({
url: 'crmInstrument/sellFunnelBusinessList',
url: 'crm/index/businessList',
method: 'post',
data: data,
headers: {
@ -251,44 +251,55 @@ export function crmIndexUnContactCustomerAPI(data) {
//* **********************************
/**
*
* 跟进记录导出
* @param {*} data
*/
export function crmInstrumentExportRecordListAPI(data) {
var param = new FormData()
Object.keys(data).forEach(key => {
param.append(key, data[key])
})
return request({
url: '',
url: 'crm/activity/excelExport',
method: 'post',
data: data,
data: param,
responseType: 'blob',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
'Content-Type': 'multipart/form-data'
},
timeout: 60000
})
}
/**
*
* 跟进记录导入
* @param {*} data
*/
export function crmInstrumentImportRecordListAPI(data) {
var param = new FormData()
Object.keys(data).forEach(key => {
param.append(key, data[key])
})
return request({
url: '',
url: 'crm/activity/excelImport',
method: 'post',
data: data,
data: param,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
'Content-Type': 'multipart/form-data'
}
})
}
/**
*
* 日志导入模板下载
* @param {*} data
*/
export function crmInstrumentDownloadRecordExcelAPI(data) {
return request({
url: '',
url: 'crm/activity/excelDownload',
method: 'post',
data: data,
responseType: 'blob',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}

@ -200,3 +200,18 @@ export function journalQueryActivityCountAPI(data) {
}
})
}
/**
* 日志点赞
* @param {*} data
*/
export function oaLogFavourOrCancelAPI(data) {
return request({
url: 'oa/log/favourUpdate',
method: 'post',
data: data,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}

@ -383,3 +383,19 @@ export function workWorkAddUserSetRoleGroupAPI(data) {
data: data
})
}
// /**
// * 项目成员权限列表
// * @param {*} data
// */
// export function (data) {
// return request({
// url: 'work/work/addUserGroup',
// method: 'post',
// headers: {
// 'Content-Type': 'application/json;charset=UTF-8'
// },
// data: data
// })
// }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -114,6 +114,7 @@
slot="footer"
class="dialog-footer">
<el-popover
v-if="config.historyShow"
v-model="historyPopoverShow"
placement="top"
width="800"
@ -181,9 +182,11 @@ import merge from '@/utils/merge'
const DefaultProps = {
typeName: '', //
ownerSelectShow: true,
historyShow: true,
ownerSelectShow: false,
poolSelectShow: false,
repeatHandleShow: true,
importParams: null, //
repeatRuleShow: true, //
importRequest: null, //
templateRequest: null //
@ -419,10 +422,12 @@ export default {
* 第一步上传
*/
firstUpdateFile(result) {
const params = {}
let params = {}
params.config = this.repeatHandling
params.file = this.file
params.owner_user_id = this.user.length > 0 ? this.user[0].id : ''
if (this.config.ownerSelectShow) {
params.owner_user_id = this.user.length > 0 ? this.user[0].id : ''
}
if (this.config.poolSelectShow) {
params.pool_id = this.pool_id
}
@ -434,6 +439,12 @@ export default {
product: crmProductExcelImportAPI
}[this.crmType]
this.loading = true
if (this.config.importParams) {
params = {
...params,
...this.config.importParams
}
}
request(params)
.then(res => {
if (result) {
@ -524,7 +535,7 @@ export default {
contacts: crmContactsDownloadExcelAPI,
product: crmProductDownloadExcelAPI
}[this.crmType]
request({ pool_id: this.config.poolSelectShow && this.pool_id })
request(this.config.templateParams || { pool_id: this.config.poolSelectShow && this.pool_id })
.then(res => {
downloadExcelWithResData(res)
})

@ -376,7 +376,6 @@ export default {
params[this.action.data.moduleType + '_id'] = this.action.data[ this.action.data.moduleType + 'Id'] || this.action.data[ this.action.data.moduleType + '_id']
if (this.action.data.params) {
for (const field in this.action.data.params) {
params[field] = this.action.data.params[field]

@ -0,0 +1,212 @@
<template>
<div class="wk-checkbox">
<template v-if="valueIsObject">
<el-select
v-if="showType === 'default'"
v-model="dataValue.select"
:disabled="disabled"
:clearable="clearable"
:placeholder="placeholder"
style="width: 100%;"
multiple
@change="valueChange">
<el-option
v-for="(item, index) in options"
:key="index"
:label="!isEmptyValue(item.value) ? item.label || item.name : item "
:value="getValue(item)"/>
</el-select>
<el-checkbox-group
v-else-if="showType === 'tiled'"
v-model="dataValue.select"
:disabled="disabled"
@change="valueChange">
<el-checkbox
v-for="(item, index) in options"
:key="index"
:label="getValue(item)">
{{ !isEmptyValue(item.value) ? item.label || item.name : item }}
</el-checkbox>
</el-checkbox-group>
<el-input
v-if="dataValue.select.includes('其他')"
v-model="dataValue.otherValue"
:disabled="disabled"
:maxlength="100"
placeholder="其他选项需填写,否则为无效选项"
@blur="inputBlur"/>
</template>
</div>
</template>
<script>
import { isObject, isEmpty } from '@/utils/types'
import { valueEquals } from 'element-ui/lib/utils/util'
import Emitter from 'element-ui/lib/mixins/emitter'
export default {
//
name: 'WkCheckbox',
components: {},
mixins: [Emitter],
props: {
// eslint-disable-next-line vue/require-prop-types
value: {},
// input
otherShowInput: {
type: Boolean,
default: false
},
disabled: Boolean,
clearable: Boolean,
placeholder: String,
options: {
type: Array,
default: () => {
return []
}
},
showType: {
type: String,
default: 'default' // default tiled
}
},
data() {
return {
dataValue: {
select: [],
otherValue: ''
}
}
},
computed: {
valueIsObject() {
return isObject(this.dataValue)
}
},
watch: {
value: {
handler(newVal, oldVal) {
this.validValue()
},
immediate: true
}
},
created() {
},
mounted() {},
beforeDestroy() {},
methods: {
/**
* 验证值
*/
validValue() {
if (isEmpty(this.value)) {
this.dataValue = {
select: [],
otherValue: ''
}
} else {
if (this.otherShowInput) {
const otherItem = this.value.filter((name) => !this.options.includes(name))
if (otherItem.length > 0) {
const newValue = this.value.filter((name) => !otherItem.includes(name))
newValue.push('其他')
this.dataValue = {
select: newValue,
otherValue: otherItem[otherItem.length - 1]
}
} else {
if (!valueEquals(this.value, this.dataValue.select)) {
this.dataValue = {
select: this.value,
otherValue: ''
}
}
}
} else {
this.dataValue = {
select: this.value,
otherValue: ''
}
}
}
},
/**
* 选项值
*/
getValue(item) {
return !this.isEmptyValue(item.value) ? item.value : item
},
/**
* 判断是空值
*/
isEmptyValue(value) {
return value === null || value == undefined
},
/**
* 值更新
*/
valueChange() {
if (this.dataValue.select.includes('其他')) {
const newValue = this.dataValue.select.filter(item => item !== '其他')
newValue.push(this.dataValue.otherValue.trim())
this.$emit('input', newValue)
this.$emit('change', newValue)
this.dispatch('ElFormItem', 'el.form.change', newValue)
} else {
this.dataValue.otherValue = ''
this.$emit('input', this.dataValue.select)
this.$emit('change', this.dataValue.select)
this.dispatch('ElFormItem', 'el.form.change', this.dataValue.select)
}
},
/**
* 失去焦点
*/
inputBlur() {
this.valueChange()
// const value = this.dataValue.otherValue
// const eIsObject = this.options.length > 0 && !this.isEmptyValue(this.options[0].value)
// const has = this.options.find(item => {
// if (eIsObject) {
// return item.value === value.trim()
// } else {
// return item === value.trim()
// }
// })
// if (has) {
// this.dataValue.otherValue = ''
// }
// this.$emit('change', this.dataValue.select)
// this.$emit('input', this.dataValue.select)
}
}
}
</script>
<style lang="scss" scoped>
.wk-checkbox {
.el-input {
margin-top: 5px;
}
.el-checkbox-group {
line-height: 1.5;
}
}
</style>

@ -0,0 +1,61 @@
<template>
<div class="wk-desc-text">
<tinymce
v-bind="$attrs"
:disabled="true" :toolbar="[]" :init="{
statusbar: false,
placeholder: '描述文字内容',
content_style: ' * {color: #262626; margin: 0;} body { font-size: 14px; }',
quickbars_selection_toolbar: false,
contextmenu: '',
plugins: 'autoresize',
autoresize_bottom_margin: 0
}" />
</div>
</template>
<script>
import Tinymce from '@/components/Tinymce'
export default {
//
name: 'WkDescText',
components: {
Tinymce
},
inheritAttrs: false,
props: {},
data() {
return {
}
},
computed: {},
watch: {},
created() {},
mounted() {},
beforeDestroy() {},
methods: {}
}
</script>
<style lang="scss">
.wk-desc-text {
.tox-tinymce {
border: none;
}
.tox .tox-edit-area__iframe {
background-color: unset;
}
}
</style>

@ -0,0 +1,194 @@
<template>
<div v-if="fieldForm && fieldForm.length > 0" class="wk-detail-table-view">
<template v-if="showType === 'default'">
<div
v-for="(children, sectionIndex) in fieldList"
:key="sectionIndex"
class="detail-item">
<flexbox class="detail-item__head">
<div class="detail-item__head-title">{{ title }}{{ sectionIndex+1 }}</div>
</flexbox>
<flexbox
class="wk-form-items"
align="flex-start"
wrap="wrap"
justify="flex-start">
<template v-for="(item, index) in children">
<div
v-if="getShowValue(item)"
:key="index"
:class="[`is-${item.formType}`]"
:label="item.name"
:style="{width: item.stylePercent ? `${item.stylePercent}%` : 'auto'}"
class="wk-form-item">
<div class="wk-form-item__label">{{ item.name }}</div>
<wk-field-view
:props="item"
:form-type="item.formType"
:value="fieldForm[sectionIndex][item.field]"
>
<template slot-scope="{ data }">
<slot :data="data" />
</template>
</wk-field-view>
</div>
</template>
</flexbox>
</div>
</template>
<div
v-else-if="showType === 'table'"
class="detail-item">
<el-table
:data="fieldForm"
:row-key="Date.now().toString()"
class="wk-table-items"
style="width: 100%">
<el-table-column
v-for="(item, index) in addFieldList"
v-if="getShowValue(item)"
:key="index"
:prop="item.field"
:label="item.name"
:min-width="getMinWidth(item.formType)">
<template slot-scope="{ row, column, $index }">
<wk-field-view
:props="item"
:form-type="item.formType"
:value="row[item.field]"
>
<template slot-scope="{ data }">
<slot :data="data" />
</template>
</wk-field-view>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
export default {
// WkDetailTableView
name: 'WkDetailTableView',
components: {
WkFieldView: () => import('@/components/NewCom/WkForm/WkFieldView')
},
props: {
title: String,
showType: {
type: String,
default: 'defalut' // defalut table
},
addFieldList: Array, //
fieldForm: {
type: [Array, String],
default: () => {
return []
}
},
fieldList: { //
type: Array,
default: () => {
return []
}
}
},
data() {
return {
}
},
computed: {},
watch: {},
created() {},
mounted() {},
beforeDestroy() {},
methods: {
getMinWidth(formType) {
if (formType === 'date_interval' ||
formType === 'dateRange' ||
formType === 'file' ||
formType === 'location' ||
formType === 'position') {
return 250
}
return 150
},
/**
* 判断展示
*/
getShowValue(item) {
if (item.hasOwnProperty('show')) {
return item.show
}
return true
}
}
}
</script>
<style lang="scss" scoped>
.wk-detail-table-view {
font-size: 13px;
line-height: normal;
.detail-item {
border-radius: 3px;
border: 1px solid #e1e1e1;
background-color: white;
&__head {
padding: 10px 20px;
background-color: #f5f5f5;
&-title {
height: auto;
font-size: 12px;
color: #333;
flex: 1;
line-height: normal;
}
.el-button {
padding: 0;
}
}
}
.detail-item + .detail-item {
margin-top: 10px;
}
.wk-form-items {
padding-bottom: 10px;
}
.wk-form-item {
padding: 12px 12px 0;
margin-bottom: 0;
&__label {
line-height: 1.5;
padding-bottom: 5px;
word-break: break-all;
word-wrap: break-word;
color: #666;
}
&.is-desc_text {
.wk-form-item__label {
display: none;
}
}
}
}
</style>

@ -0,0 +1,160 @@
<template>
<el-table
:data="fieldFrom"
:row-key="Date.now().toString()"
class="wk-table-items"
style="width: 100%">
<el-table-column
label="序号"
width="50">
<template slot-scope="{ row, column, $index }">
{{ $index + 1 }}
</template>
</el-table-column>
<el-table-column
v-for="(item, index) in fieldList"
v-if="getShowValue(item)"
:key="index"
:prop="item.field"
:min-width="getMinWidth(item.formType)">
<template
slot="header"
slot-scope="scope">
<span v-if="item.isNull == 1" class="red">*</span>{{ item.name }}
</template>
<template slot-scope="{ row, column, $index }">
<wk-form-item
:prop-prefix="`${propPrefix || ''}[${$index}].`"
:item="fieldList[index]"
:index="$index"
:field-from="fieldFrom[$index]"
:disabled="disabled"
@change="fieldChange"
>
<template slot-scope="{ data, index }">
<slot :data="data" :index="$index" />
</template>
</wk-form-item>
</template>
</el-table-column>
<el-table-column
:resizable="false"
fixed="right"
label="操作"
width="60">
<template slot-scope="{ row, column, $index }">
<el-button
icon="wk wk-icon-bin" type="text" @click="deleteClick($index)"/>
</template>
</el-table-column>
</el-table>
</template>
<script>
export default {
// table
name: 'WkTableItems',
components: {
WkFormItem: () => import('../WkForm/WkFormItem')
},
props: {
//
propPrefix: {
type: String,
default: ''
},
fieldFrom: {
type: Array,
default: () => {
return []
}
},
fieldList: {
type: Array,
default: () => {
return []
}
},
disabled: Boolean
},
data() {
return {
}
},
computed: {},
watch: {},
created() {
},
mounted() {},
beforeDestroy() {},
methods: {
deleteClick(index) {
this.$emit('delete', index)
},
getMinWidth(formType) {
if (formType === 'date_interval' ||
formType === 'dateRange' ||
formType === 'file' ||
formType === 'location' ||
formType === 'position') {
return 250
}
return 150
},
/**
* 判断展示
*/
getShowValue(item) {
if (item.hasOwnProperty('show')) {
return item.show
}
return true
},
/**
* 字段change
*/
fieldChange(item, index, value, valueList) {
this.$emit('change', item, index, value, valueList)
}
}
}
</script>
<style lang="scss">
.wk-table-items {
th {
line-height: initial;
}
.wk-form-item {
padding: 8px 0 !important;
margin-bottom: 0 !important;
.el-form-item__label {
display: none;
}
width: auto !important;
}
}
</style>
<style lang="scss" scoped>
.red {
color: #F56C6C;
margin-right: 4px;
}
</style>

@ -0,0 +1,168 @@
<template>
<div class="wk-detail-table">
<template v-if="showType === 'default'">
<div
v-for="(children, index) in fieldList"
:key="index"
class="detail-item">
<flexbox class="detail-item__head">
<div class="detail-item__head-title">{{ title }}{{ index+1 }}</div>
<el-button
v-if="fieldList.length > 1"
icon="wk wk-icon-bin" type="text" @click="deleteClick(index)"/>
</flexbox>
<wk-form-items
:field-from="fieldForm[index]"
:field-list="children"
:prop-prefix="`${propPrefix || ''}[${index}].`"
:disabled="disabled"
@change="formChange"
/>
<div class="add-btn">
<el-button type="text" @click="addClick">
<i class="wk wk-l-plus" />
{{ btnName }}
</el-button>
</div>
</div>
</template>
<div
v-else-if="showType === 'table'"
class="detail-item">
<wk-table-items
:field-from="fieldForm"
:field-list="addFieldList"
:prop-prefix="propPrefix"
:disabled="disabled"
@delete="deleteClick"
@change="formChange"
/>
<div class="add-btn">
<el-button type="text" @click="addClick">
<i class="wk wk-l-plus" />
{{ btnName }}
</el-button>
</div>
</div>
</div>
</template>
<script>
import WkTableItems from './WkTableItems'
import { objDeepCopy } from '@/utils'
import Emitter from 'element-ui/lib/mixins/emitter'
export default {
//
name: 'WkDetailTable',
components: {
WkFormItems: () => import('../WkForm/WkFormItems'),
WkTableItems
},
mixins: [Emitter],
props: {
title: String,
showType: {
type: String,
default: 'defalut' // defalut table
},
propPrefix: String,
btnName: String,
addFieldList: Array,
addFieldForm: Object,
fieldForm: {
type: Array,
default: () => {
return []
}
},
fieldList: {
type: Array,
default: () => {
return []
}
},
disabled: Boolean
},
data() {
return {
}
},
computed: {},
watch: {},
created() {
},
mounted() {},
beforeDestroy() {},
methods: {
formChange(item, index, value, valueList) {
this.$emit('change', item, index, value, valueList)
this.dispatch('ElFormItem', 'el.form.change', this.fieldForm)
},
addClick() {
this.fieldList.push(objDeepCopy(this.addFieldList))
this.fieldForm.push(objDeepCopy(this.addFieldForm))
},
deleteClick(index) {
this.fieldList.splice(index, 1)
this.fieldForm.splice(index, 1)
}
}
}
</script>
<style lang="scss" scoped>
.wk-detail-table {
font-size: 14px;
line-height: inherit;
.wk-form-items {
padding: 0;
}
.detail-item {
border-radius: 3px;
border: 1px solid #e1e1e1;
background-color: white;
&__head {
padding: 10px 20px;
background-color: #f5f5f5;
&-title {
height: auto;
font-size: 12px;
color: #333;
flex: 1;
line-height: normal;
}
.el-button {
padding: 0;
}
}
}
.detail-item + .detail-item {
margin-top: 10px;
}
.add-btn {
text-align: right;
padding-right: 10px;
.wk-l-plus {
font-size: 12px;
}
}
}
</style>

@ -0,0 +1,149 @@
<template>
<div class="wk-distpicker">
<el-cascader
v-bind="$attrs"
v-model="dataValue"
:options="options"
:props="config"
@change="handleChange"/>
</div>
</template>
<script>
import DISTRICTS from '../../VDistpicker/districts'
import { valueEquals } from 'element-ui/lib/utils/util'
import { isObject } from '@/utils/types'
export default {
// WkDistpicker
name: 'WkDistpicker',
components: {},
inheritAttrs: false,
props: {
hideArea: { type: Boolean, default: false },
onlyProvince: { type: Boolean, default: false },
value: Array,
props: Object
},
data() {
return {
dataValue: []
}
},
computed: {
options() {
return this.getOptions()
},
config() {
const props = this.props || {}
return {
label: 'name',
value: 'code',
...props
}
}
},
watch: {
value: {
handler() {
this.validateValue()
},
immediate: true
}
},
created() {
},
mounted() {},
beforeDestroy() {},
methods: {
getOptions() {
const list = DISTRICTS
if (!this.onlyProvince && !this.hideArea) {
return list
}
const newList = []
list.forEach(provinceItem => {
const province = {
code: provinceItem.code,
name: provinceItem.name
}
if (!this.onlyProvince) {
province.children = []
provinceItem.children.forEach(cityItem => {
const city = {
code: cityItem.code,
name: cityItem.name
}
province.children.push(city)
if (!this.hideArea) {
city.children = cityItem.children
}
})
}
newList.push(province)
})
return newList
},
validateValue() {
if (this.value && this.value.length) {
let dataValue = this.value
if (isObject(this.value[0])) {
dataValue = this.value.map(item => item[this.config.value])
}
if (!valueEquals(dataValue, this.dataValue)) {
this.dataValue = dataValue
}
} else {
this.dataValue = []
}
},
handleChange() {
const objValue = this.getCascaderValArr(this.dataValue, this.options)
this.$emit('input', objValue)
this.$emit('change', objValue)
},
getCascaderValArr(value, data) {
const res = []
if (value.length === 0) return res
let index = 0
do {
const findRes = data.find(o => o.code === value[index])
if (findRes) {
data = findRes.children || []
res.push({
code: findRes.code,
name: findRes.name,
id: index + 1
})
}
index++
} while (index <= value.length)
return res
}
}
}
</script>
<style lang="scss" scoped>
.el-cascader {
width: 100%;
}
</style>

@ -2,13 +2,14 @@ export default {
methods: {
/**
* 判断是否为普通 整句 文本框
* @param formType 字段类型
* @param form_type 字段类型
*/
isTrimInput(formType) {
isTrimInput(form_type) {
return [
'mobile',
'email'
].includes(formType)
'email',
'website'
].includes(form_type)
},
/**
* 部门事件
@ -37,7 +38,10 @@ export default {
oldChange(dataValue, field, index) {
this.$set(this.fieldFrom, field.field, dataValue.value)
this.$emit('change', field, index, dataValue.value)
this.$refs.form.validateField(field.field)
// this.$refs.form.validateField(field.field)
if (this.$refs.form) {
this.$refs.form.validateField(field.field)
}
},
/**
@ -68,6 +72,36 @@ export default {
getTips(data) {
const tips = data.tips || data.inputTips
return tips ? `${tips}` : ''
},
/**
* 判断展示
*/
getShowValue(item) {
if (item.hasOwnProperty('show')) {
return item.show
}
return true
},
/**
* 获取类型图标
* @param {*} formType
*/
getInputIcon(formType) {
return {
mobile: 'wk wk-icon-mobile',
email: 'wk wk-icon-email-outline',
website: 'wk wk-icon-link'
}[formType]
},
/**
* 获取输入最大长度
* @param {*} formType
*/
getInputMaxlength(formType) {
if (formType === 'website') {
return 800
}
return 100
}
}
}

@ -0,0 +1,322 @@
<template>
<div v-if="ignoreFields.includes(item.field)">
<slot :data="item" :index="index" />
</div>
<el-input
v-else-if="item.form_type == 'text'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
:maxlength="100"
:placeholder="item.placeholder"
:type="item.form_type"
@input="commonChange(item, index, $event)"/>
<el-input
v-else-if="isTrimInput(item.form_type)"
v-model.trim="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
:prefix-icon="getInputIcon(item.form_type)"
:maxlength="getInputMaxlength(item.form_type)"
:placeholder="item.placeholder"
type="text"
@input="commonChange(item, index, $event)"/>
<el-input-number
v-else-if="item.form_type == 'number'"
v-model="fieldFrom[item.field]"
:placeholder="item.placeholder"
:disabled="item.disabled || disabled"
:controls="false"
@change="commonChange(item, index, $event)" />
<el-input-number
v-else-if="item.form_type == 'floatnumber'"
v-model="fieldFrom[item.field]"
:placeholder="item.placeholder"
:disabled="item.disabled || disabled"
:controls="false"
@change="commonChange(item, index, $event)" />
<wk-percent-input
v-else-if="item.form_type == 'percent'"
v-model="fieldFrom[item.field]"
:placeholder="item.placeholder"
:disabled="item.disabled || disabled"
:controls="false"
@change="commonChange(item, index, $event)" />
<el-input
v-else-if="item.form_type == 'textarea'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
:rows="3"
:autosize="{ minRows: 3}"
:maxlength="item.maxlength || 800"
:placeholder="item.placeholder"
:type="item.form_type"
resize="none"
@input="commonChange(item, index, $event)" />
<wk-select
v-else-if="['select'].includes(item.form_type)"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
:clearable="item.clearable"
:placeholder="item.placeholder"
:options="item.setting"
:show-type="item.precisions === 1 ? 'tiled' : 'default'"
:other-show-input="item.hasOwnProperty('optionsData')"
@change="commonChange(item, index, $event)"/>
<wk-checkbox
v-else-if="['checkbox'].includes(item.form_type)"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
:clearable="item.clearable"
:placeholder="item.placeholder"
:options="item.setting"
:show-type="item.precisions === 1 ? 'tiled' : 'default'"
:other-show-input="item.hasOwnProperty('optionsData')"
@change="commonChange(item, index, $event)"/>
<!-- <el-select
v-else-if="['checkbox', 'select'].includes(item.form_type)"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
:clearable="item.clearable"
:placeholder="item.placeholder"
:multiple="item.form_type === 'checkbox'"
style="width: 100%;"
@change="commonChange(item, index, $event)">
<el-option
v-for="(item, index) in item.setting"
:key="index"
:label="!isEmptyValue(item.value) ? item.label || item.name : item "
:value="!isEmptyValue(item.value) ? item.value : item"/>
</el-select> -->
<!-- <el-select
v-else-if="item.form_type == 'checkbox'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
:clearable="item.clearable"
:placeholder="item.placeholder"
multiple
style="width: 100%;"
@change="commonChange(item, index, $event)">
<el-option
v-for="(item, index) in item.setting"
:key="index"
:label="!isEmptyValue(item.value) ? item.label || item.name : item "
:value="!isEmptyValue(item.value) ? item.value : item"/>
</el-select> -->
<el-date-picker
v-else-if="item.form_type == 'date'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
clearable
style="width: 100%;"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择日期"
@change="commonChange(item, index, $event)"/>
<el-date-picker
v-else-if="item.form_type == 'dateRange'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
:type="item.dateType || 'daterange'"
:value-format="item.dateValueFormat || 'yyyy-MM-dd'"
clearable
style="width: 100%;vertical-align: middle;"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="commonChange(item, index, $event)"/>
<el-date-picker
v-else-if="item.form_type == 'datetime'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
clearable
style="width: 100%;"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="选择日期"
@change="commonChange(item, index, $event)"/>
<wk-dep-select
v-else-if="item.form_type == 'structure'"
v-model="fieldFrom[item.field]"
:request="item.request"
:props="item.props"
:params="item.params"
:disabled="item.disabled || disabled"
:radio="!isEmptyValue(item.radio) ? item.radio : true"
style="width: 100%;"
@change="depOrUserChange(item, index, arguments[0], arguments[1])"
/>
<wk-user-select
v-else-if="['single_user', 'user'].includes(item.form_type)"
v-model="fieldFrom[item.field]"
:request="item.request"
:props="item.props"
:params="item.params"
:disabled="item.disabled || disabled"
:radio="!isEmptyValue(item.radio) ? item.radio : true"
style="width: 100%;"
@change="depOrUserChange(item, index, arguments[0], arguments[1])"
/>
<el-radio-group
v-else-if="item.form_type == 'radio'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
:placeholder="item.placeholder"
@change="commonChange(item, index, $event)">
<el-radio
v-for="(item, index) in item.setting"
:key="index"
:label="!isEmptyValue(item.value) ? item.value : item">
{{ !isEmptyValue(item.value) ? item.label || item.name : item }}
</el-radio>
</el-radio-group>
<el-switch
v-else-if="item.form_type == 'boolean_value'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
active-value="1"
inactive-value="0"
@change="commonChange(item, index, $event)"/>
<wk-position
v-else-if="item.form_type == 'position'"
:hide-area="item.hideArea"
:only-province="item.onlyProvince"
:show-detail="item.showDetail"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
@change="commonChange(item, index, $event)"/>
<wk-location
v-else-if="item.form_type == 'location'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
@change="commonChange(item, index, $event)"/>
<wk-signature-pad
v-else-if="item.form_type == 'handwriting_sign'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled || disabled"/>
<wk-desc-text
v-else-if="item.form_type == 'desc_text'"
:value="fieldFrom[item.field]"/>
<el-date-picker
v-else-if="item.form_type === 'date_interval'"
v-model="fieldFrom[item.field]"
:type="item.dateType || 'daterange'"
:value-format="item.dateValueFormat || 'yyyy-MM-dd'"
:disabled="item.disabled || disabled"
style="width: 100%;vertical-align: middle;"
clearable
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="commonChange(item, index, $event)"/>
<v-distpicker
v-else-if="item.form_type == 'address'"
:province="fieldFrom[item.field].province"
:city="fieldFrom[item.field].city"
:area="fieldFrom[item.field].area"
@province="selectProvince($event, item, index)"
@city="selectCity($event, item, index)"
@area="selectArea($event, item, index)"/>
<xh-files
v-else-if="item.form_type == 'file'"
:value="fieldFrom[item.field]"
:disabled="item.disabled || disabled"
@value-change="oldChange($event, item, index)"
/>
<wk-detail-table
v-else-if="item.form_type == 'detail_table'"
:show-type="item.precisions === 2 ? 'table' : 'default'"
:title="item.name"
:prop-prefix="item.field"
:btn-name="item.remark"
:add-field-list="item.fieldExtendList"
:add-field-form="item.fieldForm"
:field-form="fieldFrom[item.field]"
:field-list="item.fieldList"
:disabled="item.disabled || disabled"/>
<div v-else>
<slot :data="item" :index="index" />
</div>
</template>
<script>
import WkUserSelect from '@/components/NewCom/WkUserSelect'
import WkDepSelect from '@/components/NewCom/WkDepSelect'
import WkPosition from '@/components/NewCom/WkPosition'
import WkLocation from '@/components/NewCom/WkLocation'
import WkSignaturePad from '@/components/NewCom/WkSignaturePad'
import WkDescText from '@/components/NewCom/WkDescText'
import WkPercentInput from '@/components/NewCom/WkPercentInput'
import WkSelect from '@/components/NewCom/WkSelect'
import WkCheckbox from '@/components/NewCom/WkCheckbox'
import WkDetailTable from '@/components/NewCom/WkDetailTable'
import VDistpicker from '@/components/VDistpicker'
import { XhFiles } from '@/components/CreateCom'
import Mixin from './Mixin'
export default {
//
name: 'WkField',
components: {
WkUserSelect,
WkDepSelect,
WkPosition,
WkLocation,
WkSignaturePad,
WkDescText,
WkPercentInput,
WkSelect,
WkCheckbox,
WkDetailTable,
VDistpicker,
XhFiles
},
mixins: [Mixin],
props: {
item: Object,
index: Number,
fieldFrom: {
type: Object,
default: () => {
return {}
}
},
//
ignoreFields: {
type: Array,
default: () => {
return []
}
},
disabled: Boolean
},
data() {
return {
}
},
computed: {},
watch: {},
created() {},
mounted() {},
beforeDestroy() {},
methods: {}
}
</script>
<style lang="scss" scoped>
.el-input-number {
width: 100%;
/deep/ .el-input__inner {
text-align: left;
padding: 0 8px;
}
}
</style>

@ -0,0 +1,192 @@
<template>
<div
:class="`is-${form_type}`"
class="wk-field-view"
>
<template v-if="ignoreFields.includes(props.field)">
<slot :data="$props" />
</template>
<span v-else-if="isCommonType">{{ getCommonShowValue() }}</span>
<el-switch
v-else-if="form_type == 'boolean_value'"
:value="value"
disabled
active-value="1"
inactive-value="0"
/>
<wk-signature-image
v-else-if="form_type == 'handwriting_sign'"
:src=" !!value ? value.url:''"
:height="config.signatureHeight"
/>
<wk-desc-text
v-else-if="form_type == 'desc_text'"
:key="Date.now().toString()"
:value="value"
/>
<span
v-else-if="form_type == 'location'"
:class="{'can-check':objectHasValue(value, 'address')}"
@click.stop="mapViewShow=true"
>{{ objectHasValue(value, 'address') ? value.address : '--' }}</span>
<span
v-else-if="form_type == 'website'"
:class="{'can-check': !isEmpty}"
@click.stop="openUrl(value)"
>{{ value }}</span>
<file-list-view
v-else-if="form_type == 'file'"
:list="value || []"
/>
<wk-detail-table-view
v-else-if="form_type == 'detail_table'"
:show-type="props.precisions === 2 ? 'table' : 'default'"
:title="props.name"
:add-field-list="props.fieldExtendList"
:field-form="value"
:field-list="props.fieldList"
>
<template slot-scope="{ data }">
<slot :data="data" />
</template>
</wk-detail-table-view>
<template v-else>
<slot :data="$props" />
</template>
<map-view
v-if="mapViewShow"
:title="value.address"
:lat="value.lat"
:lng="value.lng"
@hidden="mapViewShow=false"
/>
</div>
</template>
<script>
import WkSignatureImage from '@/components/NewCom/WkSignaturePad/Image'
import WkDescText from '@/components/NewCom/WkDescText'
import MapView from '@/components/MapView' //
import FileListView from '@/components/FileListView' //
import WkDetailTableView from '@/components/NewCom/WkDetailTable/View'
import merge from '@/utils/merge'
import { isObject, isEmpty } from '@/utils/types'
import { getFormFieldShowName } from './utils'
const DefaultWkFieldView = {
signatureHeight: '26px'
}
export default {
//
name: 'WkFieldView',
components: {
WkSignatureImage,
WkDescText,
MapView,
FileListView,
WkDetailTableView
},
props: {
props: Object, //
form_type: String,
value: [String, Object, Array, Number],
//
ignoreFields: {
type: Array,
default: () => {
return []
}
}
},
data() {
return {
//
mapViewShow: false
}
},
computed: {
config() {
return merge({ ...DefaultWkFieldView }, this.props || {})
},
isEmpty() {
return isEmpty(this.value)
},
isCommonType() {
return [
'text',
'textarea',
'website',
'select',
'checkbox',
'number',
'floatnumber',
'percent',
'mobile',
'email',
'date',
'datetime',
'date_interval',
'user',
'structure',
'position'
].includes(this.form_type)
}
},
watch: {},
created() {},
mounted() {},
beforeDestroy() {},
methods: {
/**
* 判断对象是否值
*/
objectHasValue(obj, key) {
if (isObject(obj)) {
return !isEmpty(obj[key])
}
return false
},
openUrl(url) {
if (!url.match(/^https?:\/\//i)) {
url = 'http://' + url
}
window.open(url)
},
/**
* 获取类型的展示值
*/
getCommonShowValue() {
return getFormFieldShowName(this.form_type, this.value, '', this.props)
}
}
}
</script>
<style lang="scss" scoped>
.wk-field-view {
overflow: hidden;
text-overflow: ellipsis;
.can-check {
color: $xr-color-primary;
cursor: pointer;
}
&.is-website {
display: inline;
}
}
</style>

@ -0,0 +1,130 @@
<template>
<el-form-item
v-if="getShowValue(item)"
:key="index"
:prop="`${propPrefix || ''}${item.field}`"
:rules="item.rules"
:class="[item.className || '', `is-${item.form_type}`]"
:style="{width: item.style_percent ? `${item.style_percent}%` : 'auto'}"
class="wk-form-item">
<template slot="label">
{{ item.name }}
<el-tooltip
v-if="item.tipType == 'tooltip'"
effect="dark"
placement="top">
<div slot="content" v-html="getTips(item)"/>
<i class="wk wk-help wk-help-tips"/>
</el-tooltip>
<span v-else style="color:#999;">
{{ getTips(item) }}
</span>
</template>
<wk-field
:item="item"
:index="index"
:field-from="fieldFrom"
:ignore-fields="ignoreFields"
:disabled="disabled"
@change="fieldChange"
>
<template slot-scope="{ data, index }">
<slot :data="data" :index="index" />
</template>
</wk-field>
</el-form-item>
</template>
<script>
import WkField from './WkField'
import Mixin from './Mixin'
export default {
// item
name: 'WkFormItem',
components: {
WkField
},
mixins: [Mixin],
props: {
//
propPrefix: {
type: String,
default: ''
},
item: Object,
index: Number,
fieldFrom: {
type: Object,
default: () => {
return {}
}
},
//
ignoreFields: {
type: Array,
default: () => {
return []
}
},
disabled: Boolean
},
data() {
return {
}
},
computed: {},
watch: {},
created() {},
mounted() {},
beforeDestroy() {},
methods: {
fieldChange(item, index, value, valueList) {
this.$emit('change', item, index, value, valueList)
}
}
}
</script>
<style lang="scss">
.wk-form-item {
.el-form-item__label {
line-height: 1.5;
padding-bottom: 8px;
word-break: break-all;
word-wrap: break-word;
color: #333;
}
.el-form-item__error {
position: relative;
top: auto;
left: auto;
}
.el-form-item.is-desc_text {
.el-form-item__label {
display: none;
}
}
}
</style>
<style lang="scss" scoped>
.wk-form-item {
padding: 12px 12px 0;
margin-bottom: 0;
}
</style>

@ -1,188 +1,45 @@
<template>
<div>
<el-form-item
v-for="(item, index) in fieldList"
:key="index"
:prop="item.field"
:class="[item.className || '', `is-${item.formType}`]"
:rules="item.rules">
<template slot="label">
{{ item.name }}
<span style="color:#999;">
{{ getTips(item) }}
</span>
</template>
<el-input
v-if="item.formType == 'text'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
:maxlength="100"
:placeholder="item.placeholder"
:type="item.formType"
@input="commonChange(item, index, $event)"/>
<el-input
v-else-if="isTrimInput(item.formType)"
v-model.trim="fieldFrom[item.field]"
:disabled="item.disabled"
:maxlength="100"
:placeholder="item.placeholder"
:type="item.formType"
@input="commonChange(item, index, $event)"/>
<el-input-number
v-else-if="item.formType == 'number'"
v-model="fieldFrom[item.field]"
:placeholder="item.placeholder"
:disabled="item.disabled"
:controls="false"
@input="commonChange(item, index, $event)" />
<el-input-number
v-else-if="item.formType == 'floatnumber'"
v-model="fieldFrom[item.field]"
:placeholder="item.placeholder"
:disabled="item.disabled"
:controls="false"
@change="commonChange(item, index, $event)" />
<el-input
v-else-if="item.formType == 'textarea'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
:autosize="{ minRows: 3}"
:maxlength="800"
:placeholder="item.placeholder"
:type="item.formType"
resize="none"
@input="commonChange(item, index, $event)" />
<el-select
v-else-if="['checkbox', 'select'].includes(item.formType)"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
:clearable="item.clearable"
:placeholder="item.placeholder"
:multiple="item.formType === 'checkbox'"
style="width: 100%;"
@change="commonChange(item, index, $event)">
<el-option
v-for="(item, index) in item.setting"
:key="index"
:label="!isEmptyValue(item.value) ? item.label || item.name : item "
:value="!isEmptyValue(item.value) ? item.value : item"/>
</el-select>
<el-select
v-else-if="item.formType == 'checkbox'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
:clearable="item.clearable"
:placeholder="item.placeholder"
multiple
style="width: 100%;"
@change="commonChange(item, index, $event)">
<el-option
v-for="(item, index) in item.setting"
:key="index"
:label="!isEmptyValue(item.value) ? item.label || item.name : item "
:value="!isEmptyValue(item.value) ? item.value : item"/>
</el-select>
<el-date-picker
v-else-if="item.formType == 'date'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
clearable
style="width: 100%;"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择日期"
@change="commonChange(item, index, $event)"/>
<el-date-picker
v-else-if="item.formType == 'dateRange'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
clearable
style="width: 100%;"
type="daterange"
value-format="yyyy-MM-dd"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="commonChange(item, index, $event)"/>
<el-date-picker
v-else-if="item.formType == 'datetime'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
clearable
style="width: 100%;"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="选择日期"
@change="commonChange(item, index, $event)"/>
<wk-dep-select
v-else-if="item.formType == 'structure'"
v-model="fieldFrom[item.field]"
:request="item.request"
:props="item.props"
:params="item.params"
:disabled="item.disabled"
:radio="!isEmptyValue(item.radio) ? item.radio : true"
style="width: 100%;"
@change="depOrUserChange(item, index, arguments[0], arguments[1])"
/>
<wk-user-select
v-else-if="['single_user', 'user'].includes(item.formType)"
v-model="fieldFrom[item.field]"
:request="item.request"
:props="item.props"
:params="item.params"
:disabled="item.disabled"
:radio="!isEmptyValue(item.radio) ? item.radio : true"
style="width: 100%;"
@change="depOrUserChange(item, index, arguments[0], arguments[1])"
/>
<el-radio-group
v-else-if="item.formType == 'radio'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
:placeholder="item.placeholder"
@change="commonChange(item, index, $event)">
<el-radio
v-for="(item, index) in item.setting"
:key="index"
:label="!isEmptyValue(item.value) ? item.value : item">
{{ !isEmptyValue(item.value) ? item.label || item.name : item }}
</el-radio>
</el-radio-group>
<v-distpicker
v-if="item.formType == 'address'"
:province="fieldFrom[item.field].province"
:city="fieldFrom[item.field].city"
:area="fieldFrom[item.field].area"
@province="selectProvince($event, item, index)"
@city="selectCity($event, item, index)"
@area="selectArea($event, item, index)"/>
<template v-else>
<slot :data="item" />
</template>
</el-form-item>
</div>
<flexbox
class="wk-form-items"
align="flex-start"
wrap="wrap"
justify="flex-start">
<template v-for="(item, index) in fieldList">
<wk-form-item
:key="index"
:prop-prefix="propPrefix"
:item="item"
:index="index"
:field-from="fieldFrom"
:ignore-fields="ignoreFields"
:disabled="disabled"
@change="fieldChange"
>
<template slot-scope="{ data, index }">
<slot :data="item" :index="index" />
</template>
</wk-form-item>
</template>
</flexbox>
</template>
<script>
import WkUserSelect from '@/components/NewCom/WkUserSelect'
import WkDepSelect from '@/components/NewCom/WkDepSelect'
import VDistpicker from '@/components/VDistpicker'
import Mixin from './Mixin'
import WkFormItem from './WkFormItem'
export default {
// form-item
// form-item
name: 'WkFormItems',
components: {
WkUserSelect,
WkDepSelect,
VDistpicker
WkFormItem
},
mixins: [Mixin],
props: {
//
propPrefix: {
type: String,
default: ''
},
fieldFrom: {
type: Object,
default: () => {
@ -194,7 +51,15 @@ export default {
default: () => {
return []
}
}
},
//
ignoreFields: {
type: Array,
default: () => {
return []
}
},
disabled: Boolean
},
data() {
@ -212,10 +77,16 @@ export default {
beforeDestroy() {},
methods: {}
methods: {
fieldChange(item, index, value, valueList) {
this.$emit('change', item, index, value, valueList)
}
}
}
</script>
<style lang="scss" scoped>
.wk-form-items {
padding: 0 12px;
}
</style>

@ -16,7 +16,7 @@
v-for="(item, index) in fieldList"
:key="index"
:prop="item.field"
:class="[item.className || '', `is-${item.formType}`]">
:class="[item.className || '', `is-${item.form_type}`]">
<template slot="label">
{{ item.name }}
<span style="color:#999;">
@ -24,15 +24,15 @@
</span>
</template>
<el-input
v-if="item.formType == 'text'"
v-if="item.form_type == 'text'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
:maxlength="100"
:placeholder="item.placeholder"
:type="item.formType"
:type="item.form_type"
@input="commonChange(item, index, $event)"/>
<el-input
v-if="isTrimInput(item.formType)"
v-if="isTrimInput(item.form_type)"
v-model.trim="fieldFrom[item.field]"
:disabled="item.disabled"
:maxlength="100"
@ -40,37 +40,37 @@
type="text"
@input="commonChange(item, index, $event)"/>
<el-input-number
v-else-if="item.formType == 'number'"
v-else-if="item.form_type == 'number'"
v-model="fieldFrom[item.field]"
:placeholder="item.placeholder"
:disabled="item.disabled"
:controls="false"
@input="commonChange(item, index, $event)" />
<el-input-number
v-else-if="item.formType == 'floatnumber'"
v-else-if="item.form_type == 'floatnumber'"
v-model="fieldFrom[item.field]"
:placeholder="item.placeholder"
:disabled="item.disabled"
:controls="false"
@change="commonChange(item, index, $event)" />
<el-input
v-else-if="item.formType == 'textarea'"
v-else-if="item.form_type == 'textarea'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
:rows="3"
:autosize="{ minRows: 3}"
:maxlength="800"
:placeholder="item.placeholder"
:type="item.formType"
:type="item.form_type"
resize="none"
@input="commonChange(item, index, $event)" />
<el-select
v-else-if="['checkbox', 'select'].includes(item.formType)"
v-else-if="['checkbox', 'select'].includes(item.form_type)"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
:clearable="item.clearable"
:placeholder="item.placeholder"
:multiple="item.formType === 'checkbox'"
:multiple="item.form_type === 'checkbox'"
style="width: 100%;"
@change="commonChange(item, index, $event)">
<el-option
@ -80,7 +80,7 @@
:value="!isEmptyValue(item.value) ? item.value : item"/>
</el-select>
<el-select
v-else-if="item.formType == 'checkbox'"
v-else-if="item.form_type == 'checkbox'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
:clearable="item.clearable"
@ -95,7 +95,7 @@
:value="!isEmptyValue(item.value) ? item.value : item"/>
</el-select>
<el-date-picker
v-else-if="item.formType == 'date'"
v-else-if="item.form_type == 'date'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
clearable
@ -105,7 +105,7 @@
placeholder="选择日期"
@change="commonChange(item, index, $event)"/>
<el-date-picker
v-else-if="item.formType == 'dateRange'"
v-else-if="item.form_type == 'dateRange'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
:type="item.dateType || 'daterange'"
@ -116,7 +116,7 @@
end-placeholder="结束日期"
@change="commonChange(item, index, $event)"/>
<el-date-picker
v-else-if="item.formType == 'datetime'"
v-else-if="item.form_type == 'datetime'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
clearable
@ -126,7 +126,7 @@
placeholder="选择日期"
@change="commonChange(item, index, $event)"/>
<wk-dep-select
v-else-if="item.formType == 'structure'"
v-else-if="item.form_type == 'structure'"
v-model="fieldFrom[item.field]"
:request="item.request"
:props="item.props"
@ -137,7 +137,7 @@
@change="depOrUserChange(item, index, arguments[0], arguments[1])"
/>
<wk-user-select
v-else-if="['single_user', 'user'].includes(item.formType)"
v-else-if="['single_user', 'user'].includes(item.form_type)"
v-model="fieldFrom[item.field]"
:request="item.request"
:props="item.props"
@ -148,7 +148,7 @@
@change="depOrUserChange(item, index, arguments[0], arguments[1])"
/>
<el-radio-group
v-else-if="item.formType == 'radio'"
v-else-if="item.form_type == 'radio'"
v-model="fieldFrom[item.field]"
:disabled="item.disabled"
:placeholder="item.placeholder"
@ -161,7 +161,7 @@
</el-radio>
</el-radio-group>
<v-distpicker
v-if="item.formType == 'address'"
v-if="item.form_type == 'address'"
:province="fieldFrom[item.field].province"
:city="fieldFrom[item.field].city"
:area="fieldFrom[item.field].area"
@ -169,7 +169,7 @@
@city="selectCity($event, item, index)"
@area="selectArea($event, item, index)"/>
<xh-files
v-if="item.formType == 'file'"
v-if="item.form_type == 'file'"
:value="fieldFrom[item.field]"
:disabled="item.disabled"
@value-change="oldChange($event, item, index)"

@ -0,0 +1,62 @@
import { isArray, isObject, isEmpty } from '@/utils/types'
import CheckStatusMixin from '@/mixins/CheckStatusMixin'
import CustomFieldsMixin from '@/mixins/CustomFields'
import { separator } from '@/filters/vueNumeralFilter/filters'
import { getWkDateTime } from '@/utils'
/**
* 获取自定义字段展示值
* @param {*} form_type
* @param {*} value
* @param {*} placeholder
* @param {*} item 自定义字段模型
* @returns 字符串
*/
export function getFormFieldShowName(form_type, value, placeholder = '--', item) {
if (form_type === 'position') {
return isArray(value) ? value.map(item => item.name).join() : placeholder
} else if (form_type === 'floatnumber') {
return isEmpty(value) ? '' : separator(value)
} else if (form_type === 'date') {
return getWkDateTime(value)
} else if (form_type === 'location') {
return isObject(value) ? value.address : placeholder
} else if (form_type === 'date_interval') {
return isArray(value) ? value.join('-') : placeholder
} else if (form_type === 'percent') {
return isEmpty(value) ? placeholder : `${value}%`
} else if (form_type === 'single_user') {
if (isObject(value)) {
return value.realname || placeholder
}
return value || placeholder
} else if (form_type === 'select') {
const newValue = CustomFieldsMixin.methods.getRealParams({ form_type }, value)
if (isEmpty(newValue)) {
return placeholder
} else {
return newValue
}
} else if (form_type === 'checkbox') {
const newValue = CustomFieldsMixin.methods.getRealParams({ form_type }, value)
if (isEmpty(newValue)) {
return placeholder
} else {
return newValue
}
} else if (form_type === 'structure') {
if (isArray(value)) {
return value.map(item => item.name).join() || placeholder
}
return value || placeholder
} else if (form_type === 'user') {
if (isArray(value)) {
return value.map(item => item.realname).join() || placeholder
}
return value || placeholder
} else if (form_type === 'check_status') {
return CheckStatusMixin.methods.getStatusName(value)
}
return isEmpty(value) ? placeholder : value
}

@ -0,0 +1,133 @@
<template>
<div
class="wk-location"
@mouseenter="hovering = true"
@mouseleave="hovering = false">
<el-input
v-model="dataValue.address"
:disabled="disabled"
readonly
clearable
placeholder="点击定位"
@click.native="inputClick"
@clear="inputClear">
<template slot="suffix">
<i
v-if="dataValue.address && hovering"
class="el-icon-circle-close"
style="cursor: pointer;"
@click.stop="inputClear" />
<i v-else class="wk wk-icon-location" />
</template>
</el-input>
<wk-location-point-dialog
:value="dataValue"
:visible.sync="pointDialogVisible"
@select="pointSelect"
/>
</div>
</template>
<script>
import WkLocationPointDialog from '../WkLocationPointDialog'
import { isObject } from '@/utils/types'
import { valueEquals } from 'element-ui/lib/utils/util'
import Emitter from 'element-ui/lib/mixins/emitter'
export default {
//
name: 'WkLocation',
components: {
WkLocationPointDialog
},
mixins: [Emitter],
props: {
// eslint-disable-next-line vue/require-prop-types
value: {
required: true
},
disabled: Boolean
},
data() {
return {
hovering: false,
dataValue: this.getDefaultValue(),
pointDialogVisible: false
}
},
computed: {},
watch: {
value: {
handler(val) {
if (!valueEquals(val, this.dataValue)) {
if (isObject(this.value)) {
this.dataValue = val
} else {
this.dataValue = this.getDefaultValue()
this.$emit('input', this.dataValue)
}
}
},
immediate: true
}
},
created() {
},
mounted() {},
beforeDestroy() {},
methods: {
getDefaultValue() {
return {
lat: '',
lng: '',
address: ''
}
},
inputClick() {
if (!this.disabled) {
this.pointDialogVisible = true
}
},
inputClear() {
this.dataValue = this.getDefaultValue()
this.valueChange()
},
pointSelect(data) {
if (data) {
this.dataValue = {
lat: data.point.lat,
lng: data.point.lng,
address: data.address + data.title
}
}
this.valueChange()
},
valueChange() {
this.$emit('input', this.dataValue)
this.$emit('change', this.dataValue)
this.dispatch('ElFormItem', 'el.form.change', this.dataValue)
}
}
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,210 @@
<template>
<el-dialog
:visible="visible"
:close-on-click-modal="false"
append-to-body
title="选择位置"
width="500px"
@close="close">
<flexbox align="stretch">
<flexbox-item>
<div class="area-title">定位</div>
<el-autocomplete
v-model="searchInput"
:fetch-suggestions="querySearchAsync"
style="width: 100%;"
placeholder="请输入详细位置名称"
@blur="inputBlur"
@focus="inputFocus"
@select="handleSelect">
<template slot-scope="{ item }">
<div class="name">{{ item.address + item.title }}</div>
</template>
</el-autocomplete>
<div
ref="myMap"
class="map"/>
</flexbox-item>
</flexbox>
<span slot="footer" class="dialog-footer">
<el-button @click="close"> </el-button>
<el-button type="primary" @click="selectSure"> </el-button>
</span>
</el-dialog>
</template>
<script type="text/javascript">
import VDistpicker from '@/components/VDistpicker'
import { getBaiduMap } from '@/utils'
import { isEmpty } from '@/utils/types'
export default {
name: 'WkLocationPointDialog',
components: {
VDistpicker
},
props: {
visible: {
type: Boolean,
default: false
},
value: {
type: Object,
default: () => {
return {}
}
}
},
data() {
return {
map: null,
searchInput: '', //
searchCopyInput: '', //
pointAddress: null //
}
},
computed: {},
watch: {
visible(val) {
if (val) {
this.$nextTick(() => {
getBaiduMap()
.then(() => {
const map = new BMap.Map(this.$refs.myMap, { enableMapClick: true })
let point = new BMap.Point(116.404, 39.915)
if (this.value) {
if (!isEmpty(this.value.lat) && !isEmpty(this.value.lng)) {
point = new BMap.Point(this.value.lng, this.value.lat)
}
if (!isEmpty(this.value.address)) {
this.searchInput = this.value.address
this.searchCopyInput = this.searchInput
}
}
if (!this.value || (
this.value && isEmpty(this.value.lat) && isEmpty(this.value.lng) &&
isEmpty(this.value.address)
)) {
const self = this
//
var geolocation = new BMap.Geolocation()
geolocation.getCurrentPosition(function(r) {
if (this.getStatus() == BMAP_STATUS_SUCCESS) {
var myGeo = new BMap.Geocoder({ extensions_town: true })
//
myGeo.getLocation(r.point, function(result) {
if (result) {
self.searchInput = result.address
self.searchCopyInput = result.address
self.addMarkerLabel(result.point)
result.title = ''
self.pointAddress = result
}
})
}
})
}
map.centerAndZoom(point, 14)
map.enableScrollWheelZoom()
this.map = map
this.addMarkerLabel(point)
})
})
}
}
},
mounted() {
},
methods: {
querySearchAsync(queryString, cb) {
if (queryString) {
var options = {
onSearchComplete: function(results) {
if (local.getStatus() == BMAP_STATUS_SUCCESS) {
var address = []
for (var i = 0; i < results.getCurrentNumPois(); i++) {
address.push(results.getPoi(i))
}
cb(address)
} else {
cb([])
}
},
pageCapacity: 20
}
var local = new BMap.LocalSearch(this.map, options)
local.search(queryString)
} else {
cb([])
}
},
/**
* 搜索结果选择
**/
handleSelect(item) {
this.searchInput = item.address + item.title
this.searchCopyInput = this.searchInput //
this.addMarkerLabel(item.point)
this.pointAddress = item
},
/**
* Input 失去焦点 searchInput 只能通过选择更改
**/
inputBlur() {
if (this.searchCopyInput !== this.searchInput) {
this.searchInput = this.searchCopyInput
}
},
inputFocus() {
this.searchCopyInput = this.searchInput
},
/**
* 创建标注
*/
addMarkerLabel(point) {
this.map.clearOverlays()
this.map.centerAndZoom(point, 14)
this.map.addOverlay(new BMap.Marker(point))
},
/**
* 关闭
*/
close() {
this.$emit('update:visible', false)
},
/**
* 确定选择
*/
selectSure() {
this.$emit('select', this.pointAddress)
this.close()
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.map {
height: 150px;
width: 100%;
overflow: hidden;
margin-top: 5px;
}
.area-title {
font-size: 12px;
color: #aaa;
padding-bottom: 10px;
}
.distpicker-address-wrapper /deep/ select {
height: 34px;
font-size: 12px;
border-radius: 0.1rem;
}
/deep/ .el-dialog__body {
padding: 10px 20px 20px;
}
</style>

@ -0,0 +1,281 @@
<template>
<div
:class="[
'el-input-number',
inputNumberSize ? 'el-input-number--' + inputNumberSize : '',
{ 'is-disabled': inputNumberDisabled },
{ 'is-without-controls': !controls },
{ 'is-controls-right': controlsAtRight }
]"
@dragstart.prevent>
<span
v-repeat-click="decrease"
v-if="controls"
:class="{'is-disabled': minDisabled}"
class="el-input-number__decrease"
role="button"
@keydown.enter="decrease">
<i :class="`el-icon-${controlsAtRight ? 'arrow-down' : 'minus'}`"/>
</span>
<span
v-repeat-click="increase"
v-if="controls"
:class="{'is-disabled': maxDisabled}"
class="el-input-number__increase"
role="button"
@keydown.enter="increase">
<i :class="`el-icon-${controlsAtRight ? 'arrow-up' : 'plus'}`"/>
</span>
<el-input
ref="input"
:value="displayValue"
:placeholder="placeholder"
:disabled="inputNumberDisabled"
:size="inputNumberSize"
:max="max"
:min="min"
:name="name"
:label="label"
@keydown.up.native.prevent="increase"
@keydown.down.native.prevent="decrease"
@blur="handleBlur"
@focus="handleFocus"
@input="handleInput"
@change="handleInputChange">
<template slot="suffix">%</template>
</el-input>
</div>
</template>
<script>
import Focus from 'element-ui/src/mixins/focus'
import RepeatClick from 'element-ui/src/directives/repeat-click'
export default {
name: 'WkPercentInput',
directives: {
RepeatClick
},
mixins: [Focus('input')],
inject: {
elForm: {
default: ''
},
elFormItem: {
default: ''
}
},
props: {
step: {
type: Number,
default: 1
},
stepStrictly: {
type: Boolean,
default: false
},
max: {
type: Number,
default: Infinity
},
min: {
type: Number,
default: -Infinity
},
// eslint-disable-next-line vue/require-prop-types
value: {},
disabled: Boolean,
size: String,
controls: {
type: Boolean,
default: true
},
controlsPosition: {
type: String,
default: ''
},
name: String,
label: String,
placeholder: String,
precision: {
type: Number,
validator(val) {
return val >= 0 && val === parseInt(val, 10)
}
}
},
data() {
return {
currentValue: 0,
userInput: null
}
},
computed: {
minDisabled() {
return this._decrease(this.value, this.step) < this.min
},
maxDisabled() {
return this._increase(this.value, this.step) > this.max
},
numPrecision() {
const { value, step, getPrecision, precision } = this
const stepPrecision = getPrecision(step)
if (precision !== undefined) {
if (stepPrecision > precision) {
console.warn('[Element Warn][InputNumber]precision should not be less than the decimal places of step')
}
return precision
} else {
return Math.max(getPrecision(value), stepPrecision)
}
},
controlsAtRight() {
return this.controls && this.controlsPosition === 'right'
},
_elFormItemSize() {
return (this.elFormItem || {}).elFormItemSize
},
inputNumberSize() {
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size
},
inputNumberDisabled() {
return this.disabled || !!(this.elForm || {}).disabled
},
displayValue() {
if (this.userInput !== null) {
return this.userInput
}
let currentValue = this.currentValue
if (typeof currentValue === 'number') {
if (this.stepStrictly) {
const stepPrecision = this.getPrecision(this.step)
const precisionFactor = Math.pow(10, stepPrecision)
currentValue = Math.round(currentValue / this.step) * precisionFactor * this.step / precisionFactor
}
if (this.precision !== undefined) {
currentValue = currentValue.toFixed(this.precision)
}
}
return currentValue
}
},
watch: {
value: {
immediate: true,
handler(value) {
let newVal = value === undefined ? value : Number(value)
if (newVal !== undefined) {
if (isNaN(newVal)) {
return
}
if (this.stepStrictly) {
const stepPrecision = this.getPrecision(this.step)
const precisionFactor = Math.pow(10, stepPrecision)
newVal = Math.round(newVal / this.step) * precisionFactor * this.step / precisionFactor
}
if (this.precision !== undefined) {
newVal = this.toPrecision(newVal, this.precision)
}
}
if (newVal >= this.max) newVal = this.max
if (newVal <= this.min) newVal = this.min
this.currentValue = newVal
this.userInput = null
this.$emit('input', newVal)
}
}
},
mounted() {
const innerInput = this.$refs.input.$refs.input
innerInput.setAttribute('role', 'spinbutton')
innerInput.setAttribute('aria-valuemax', this.max)
innerInput.setAttribute('aria-valuemin', this.min)
innerInput.setAttribute('aria-valuenow', this.currentValue)
innerInput.setAttribute('aria-disabled', this.inputNumberDisabled)
},
updated() {
if (!this.$refs || !this.$refs.input) return
const innerInput = this.$refs.input.$refs.input
innerInput.setAttribute('aria-valuenow', this.currentValue)
},
methods: {
toPrecision(num, precision) {
if (precision === undefined) precision = this.numPrecision
return parseFloat(Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision))
},
getPrecision(value) {
if (value === undefined) return 0
const valueString = value.toString()
const dotPosition = valueString.indexOf('.')
let precision = 0
if (dotPosition !== -1) {
precision = valueString.length - dotPosition - 1
}
return precision
},
_increase(val, step) {
if (typeof val !== 'number' && val !== undefined) return this.currentValue
const precisionFactor = Math.pow(10, this.numPrecision)
// Solve the accuracy problem of JS decimal calculation by converting the value to integer.
return this.toPrecision((precisionFactor * val + precisionFactor * step) / precisionFactor)
},
_decrease(val, step) {
if (typeof val !== 'number' && val !== undefined) return this.currentValue
const precisionFactor = Math.pow(10, this.numPrecision)
return this.toPrecision((precisionFactor * val - precisionFactor * step) / precisionFactor)
},
increase() {
if (this.inputNumberDisabled || this.maxDisabled) return
const value = this.value || 0
const newVal = this._increase(value, this.step)
this.setCurrentValue(newVal)
},
decrease() {
if (this.inputNumberDisabled || this.minDisabled) return
const value = this.value || 0
const newVal = this._decrease(value, this.step)
this.setCurrentValue(newVal)
},
handleBlur(event) {
this.$emit('blur', event)
},
handleFocus(event) {
this.$emit('focus', event)
},
setCurrentValue(newVal) {
const oldVal = this.currentValue
if (typeof newVal === 'number' && this.precision !== undefined) {
newVal = this.toPrecision(newVal, this.precision)
}
if (newVal >= this.max) newVal = this.max
if (newVal <= this.min) newVal = this.min
if (oldVal === newVal) return
this.userInput = null
this.$emit('input', newVal)
this.$emit('change', newVal, oldVal)
this.currentValue = newVal
},
handleInput(value) {
this.userInput = value
},
handleInputChange(value) {
const newVal = value === '' ? undefined : Number(value)
if (!isNaN(newVal) || value === '') {
this.setCurrentValue(newVal)
}
this.userInput = null
},
select() {
this.$refs.input.select()
}
}
}
</script>

@ -0,0 +1,139 @@
<template>
<div class="wk-position">
<wk-distpicker
:hide-area="hideArea"
:only-province="onlyProvince"
:disabled="disabled"
v-bind="$attrs"
v-model="distpickerValue"
clearable
@change="valueChange"/>
<el-input
v-if="showDetail"
:rows="2"
:disabled="disabled"
v-model="detailAddress"
:maxlength="100"
type="textarea"
placeholder="详细地址"
@change="valueChange"/>
</div>
</template>
<script>
import WkDistpicker from '../WkDistpicker'
import { isArray, isEmpty } from '@/utils/types'
import Emitter from 'element-ui/lib/mixins/emitter'
export default {
//
name: 'WkPosition',
components: {
WkDistpicker
},
mixins: [Emitter],
inheritAttrs: false,
props: {
hideArea: { type: Boolean, default: false },
onlyProvince: { type: Boolean, default: false },
showDetail: { type: Boolean, default: true },
// eslint-disable-next-line vue/require-prop-types
value: {
required: true
},
disabled: Boolean
},
data() {
return {
distpickerValue: [],
detailAddress: '',
dataValue: []
}
},
computed: {},
watch: {
value: {
handler(val) {
if (!this.valueEquals(val, this.dataValue)) {
this.getDefaultValue()
}
},
immediate: true
}
},
created() {},
mounted() {},
beforeDestroy() {},
methods: {
getDefaultValue() {
const value = isArray(this.value) ? this.value : []
const distpickerValue = []
let hasAddress = false
value.forEach(item => {
if (item.id === 4) {
hasAddress = true
this.detailAddress = item.name
} else {
distpickerValue.push(item)
}
})
if (!hasAddress) {
this.detailAddress = ''
}
this.distpickerValue = distpickerValue
},
/**
* 值更新
*/
valueChange() {
const dataValue = this.showDetail && isEmpty(this.detailAddress) ? this.distpickerValue : this.distpickerValue.concat([{
code: '',
name: this.detailAddress,
id: 4
}])
this.dataValue = dataValue
this.$emit('input', this.dataValue)
this.$emit('change', this.dataValue)
this.dispatch('ElFormItem', 'el.form.change', this.dataValue)
},
valueEquals(a, b) {
if (a === b) return true
if (!(a instanceof Array)) return false
if (!(b instanceof Array)) return false
if (a.length !== b.length) return false
for (let i = 0; i !== a.length; ++i) {
const aValue = a[i]
const bValue = b[i]
if (aValue.id !== bValue.id || aValue.name !== bValue.name) return false
}
return true
}
}
}
</script>
<style lang="scss" scoped>
.wk-position {
.el-textarea {
margin-top: 15px;
}
}
</style>

@ -0,0 +1,205 @@
<template>
<div class="wk-select">
<template v-if="valueIsObject">
<el-select
v-if="showType === 'default'"
v-model="dataValue.select"
:disabled="disabled"
:clearable="clearable"
:placeholder="placeholder"
style="width: 100%;"
@change="valueChange">
<el-option
v-for="(item, index) in options"
:key="index"
:label="!isEmptyValue(item.value) ? item.label || item.name : item "
:value="getValue(item)"/>
</el-select>
<el-radio-group
v-else-if="showType === 'tiled'"
v-model="dataValue.select"
@change="valueChange">
<el-radio
v-for="(item, index) in options"
:key="index"
:label="getValue(item)">
{{ !isEmptyValue(item.value) ? item.label || item.name : item }}
</el-radio>
</el-radio-group>
<el-input
v-if="dataValue.select === '其他' && otherShowInput"
v-model="dataValue.otherValue"
:disabled="disabled"
:maxlength="100"
placeholder="其他选项需填写,否则为无效选项"
@blur="inputBlur"/>
</template>
</div>
</template>
<script>
import { isObject, isEmpty } from '@/utils/types'
import Emitter from 'element-ui/lib/mixins/emitter'
export default {
//
name: 'WkSelect',
components: {},
mixins: [Emitter],
props: {
value: [String, Number],
// input
otherShowInput: {
type: Boolean,
default: false
},
disabled: Boolean,
clearable: Boolean,
placeholder: String,
options: {
type: Array,
default: () => {
return []
}
},
showType: {
type: String,
default: 'default' // default tiled
}
},
data() {
return {
dataValue: {
select: '',
otherValue: ''
}
}
},
computed: {
valueIsObject() {
return isObject(this.dataValue)
}
},
watch: {
value: {
handler(newVal, oldVal) {
this.validValue()
},
immediate: true
}
},
created() {
// this.validValue()
},
mounted() {},
beforeDestroy() {},
methods: {
/**
* 验证值
*/
validValue() {
if (this.value !== this.dataValue.select && this.value !== this.dataValue.otherValue) {
if (isEmpty(this.value)) {
this.dataValue = {
select: '',
otherValue: ''
}
} else {
if (this.otherShowInput) {
const valueObj = this.options.find(item => this.getValue(item) === this.value)
if (valueObj) {
if (this.dataValue.select !== this.value) {
this.dataValue = {
select: this.value,
otherValue: ''
}
}
} else {
if (this.dataValue.otherValue !== this.value) {
this.dataValue = {
select: '其他',
otherValue: this.value
}
}
}
} else {
this.dataValue = {
select: this.value,
otherValue: ''
}
}
}
}
},
/**
* 选项值
*/
getValue(item) {
return !this.isEmptyValue(item.value) ? item.value : item
},
/**
* 判断是空值
*/
isEmptyValue(value) {
return value === null || value == undefined
},
/**
* 值更新
*/
valueChange() {
const value = this.dataValue.select === '其他' ? this.dataValue.otherValue.trim() : this.dataValue.select
this.$emit('input', value)
this.$emit('change', value)
this.dispatch('ElFormItem', 'el.form.change', value)
},
/**
* 失去焦点
*/
inputBlur() {
// inputchange
const value = this.dataValue.otherValue
const eIsObject = this.options.length > 0 && !this.isEmptyValue(this.options[0].value)
const has = this.options.find(item => {
if (eIsObject) {
return item.value === value.trim()
} else {
return item === value.trim()
}
})
if (has) {
this.dataValue.otherValue = ''
}
this.$emit('input', this.dataValue.otherValue)
this.$emit('change', this.dataValue.otherValue)
this.dispatch('ElFormItem', 'el.form.change', this.dataValue.otherValue)
}
}
}
</script>
<style lang="scss" scoped>
.wk-select {
.el-input {
margin-top: 5px;
}
.el-radio {
margin: 5px 30px 5px 0;
}
}
</style>

@ -0,0 +1,97 @@
<template>
<el-popover
:disabled="!hasContent"
placement="bottom"
width="200"
trigger="hover">
<el-image
:src="url"
:style="styleObj"
style="width: 100%; height: 100%"
fit="contain"/>
<div slot="reference">
<el-image
v-if="hasContent"
:src="url"
:style="{height: height, ...styleObj}"
style="width: 100%;"
fit="contain"/>
<template v-else>--</template>
</div>
</el-popover>
</template>
<script>
// import { adminFileQueryOneByBatchIdAPI } from '@/api/admin/file'
export default {
// WkSignatureImage
name: 'WkSignatureImage',
components: {},
props: {
height: {
type: String,
default: '100%'
},
src: String // batchId
},
data() {
return {
url: '',
styleObj: {}
}
},
computed: {
hasContent() {
return !!this.url
}
},
watch: {
src(newVal, oldVal) {
if (newVal) {
this.getData()
} else {
this.url = ''
}
}
},
created() {
if (this.src) {
this.getData()
}
},
mounted() {},
beforeDestroy() {},
methods: {
getData() {
// adminFileQueryOneByBatchIdAPI(this.src).then(res => {
// const resData = res.data
// if (resData) {
// const url = process.env.BASE_API + resData.url
// if (this.url !== url) {
// this.url = url
// }
// } else {
// this.url = ''
// }
// }).catch(() => {
// })
this.url = this.src
}
}
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,196 @@
<script>
import SignaturePad from 'signature_pad'
// import mergeImages from 'merge-images'
import {
DEFAULT_OPTIONS,
TRANSPARENT_PNG,
IMAGE_TYPES,
checkSaveType,
convert2NonReactive
} from '../utils/index'
export default {
name: 'VueSignaturePad',
props: {
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '100%'
},
customStyle: {
type: Object,
default: () => ({})
},
options: {
type: Object,
default: () => ({})
},
images: {
type: Array,
default: () => []
}
},
data: () => ({
signaturePad: {},
cacheImages: [],
signatureData: TRANSPARENT_PNG,
onResizeHandler: null
}),
computed: {
propsImagesAndCustomImages() {
const nonReactiveProrpImages = convert2NonReactive(this.images)
const nonReactiveCachImages = convert2NonReactive(this.cacheImages)
return [...nonReactiveProrpImages, ...nonReactiveCachImages]
},
canvas() {
return this.$refs.signaturePadCanvas
}
},
watch: {
options: function(nextOptions) {
Object.keys(nextOptions).forEach(option => {
if (this.signaturePad[option]) {
this.signaturePad[option] = nextOptions[option]
}
})
},
height() {
this.resizeCanvas()
}
},
mounted() {
const { options } = this
const canvas = this.$refs.signaturePadCanvas
const signaturePad = new SignaturePad(canvas, {
...DEFAULT_OPTIONS,
...options
})
this.signaturePad = signaturePad
this.onResizeHandler = this.resizeCanvas.bind(this)
window.addEventListener('resize', this.onResizeHandler, false)
this.resizeCanvas()
},
beforeDestroy() {
if (this.onResizeHandler) {
window.removeEventListener('resize', this.onResizeHandler, false)
}
},
methods: {
resizeCanvas() {
const canvas = this.$refs.signaturePadCanvas
const data = this.signaturePad.toData()
const ratio = Math.max(window.devicePixelRatio || 1, 1)
canvas.width = canvas.offsetWidth * ratio
canvas.height = canvas.offsetHeight * ratio
canvas.getContext('2d').scale(ratio, ratio)
this.signaturePad.clear()
this.signatureData = TRANSPARENT_PNG
this.signaturePad.fromData(data)
},
saveSignature(type = IMAGE_TYPES[0], encoderOptions) {
const { signaturePad } = this
const status = { isEmpty: false, data: undefined }
if (!checkSaveType(type)) {
const imageTypesString = IMAGE_TYPES.join(', ')
throw new Error(
`The Image type is incorrect! We are support ${imageTypesString} types.`
)
}
if (signaturePad.isEmpty()) {
return {
...status,
isEmpty: true
}
} else {
this.signatureData = signaturePad.toDataURL(type, encoderOptions)
return {
...status,
data: this.signatureData
}
}
},
undoSignature() {
const { signaturePad } = this
const record = signaturePad.toData()
if (record) {
return signaturePad.fromData(record.slice(0, -1))
}
},
// mergeImageAndSignature(customSignature) {
// this.signatureData = customSignature
// return mergeImages([
// ...this.images,
// ...this.cacheImages,
// this.signatureData
// ])
// },
// addImages(images = []) {
// this.cacheImages = [...this.cacheImages, ...images]
// return mergeImages([
// ...this.images,
// ...this.cacheImages,
// this.signatureData
// ])
// },
fromDataURL(data, options = {}, callback) {
return this.signaturePad.fromDataURL(data, options, callback)
},
fromData(data) {
return this.signaturePad.fromData(data)
},
toData() {
return this.signaturePad.toData()
},
lockSignaturePad() {
return this.signaturePad.off()
},
openSignaturePad() {
return this.signaturePad.on()
},
isEmpty() {
return this.signaturePad.isEmpty()
},
getPropImagesAndCacheImages() {
return this.propsImagesAndCustomImages
},
clearCacheImages() {
this.cacheImages = []
return this.cacheImages
},
clearSignature() {
return this.signaturePad.clear()
},
toBlob(callback, type, encoderOptions) {
this.$refs.signaturePadCanvas.toBlob(callback, type, encoderOptions)
}
},
render(createElement) {
const { width, height, customStyle } = this
const urlObj = require('./signature.png')
return createElement(
'div',
{
style: {
width,
height,
...customStyle
}
},
[
createElement('canvas', {
style: {
width: '100%',
height: '100%',
cursor: `url(${urlObj}),default`
},
ref: 'signaturePadCanvas'
})
]
)
}
}
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

@ -0,0 +1,167 @@
<template>
<div ref="wkSignaturePad" class="wk-signature-pad">
<wk-signature-image
v-if="disabled"
:src="value.url"
:height="height"
/>
<template v-else>
<vue-signature-pad
ref="signaturePad"
:key="height"
:options="options"
:height="height"
width="100%"
/>
<div class="wk-signature-pad__handle">
<el-button type="text" icon="wk wk-icon-reply" @click="handleClick('undo')"></el-button>
<el-button type="text" icon="wk wk-icon-bin" @click="handleClick('clear')"></el-button>
</div>
</template>
</div>
</template>
<script>
import { crmFileSingleSaveAPI } from '@/api/common'
import VueSignaturePad from './VueSignaturePad'
import WkSignatureImage from './Image'
import { valueEquals } from 'element-ui/lib/utils/util'
import { getImageData } from '@/utils'
export default {
//
name: 'WkSignaturePad',
components: {
VueSignaturePad,
WkSignatureImage
},
props: {
value: [Object, String], // batchId
data: String, //
disabled: Boolean
},
data() {
return {
options: {
backgroundColor: '#fff',
onEnd: this.onEnd,
minWidth: 1,
maxWidth: 3
},
height: '150px'
}
},
computed: {
},
watch: {
data(newVal, oldVal) {
if (!valueEquals(newVal, oldVal)) {
this.$refs.signaturePad.fromDataURL(newVal)
}
}
},
created() {},
mounted() {
if (this.value) {
this.getData()
} else {
this.$emit('input', {})
}
this.height = `${parseInt(this.$refs.wkSignaturePad.clientWidth / 2.6)}px`
},
beforeDestroy() {},
methods: {
getData() {
getImageData(this.value.url)
.then(data => {
var img = new Image()
img.onload = () => {
const canvasWidth = this.$refs.signaturePad.canvas.width
const width = img.width
const ratio = canvasWidth / width
console.log(width, canvasWidth, ratio)
this.options.minWidth = this.options.minWidth * ratio
this.options.maxWidth = this.options.maxWidth * ratio
this.$nextTick(() => {
this.$refs.signaturePad.fromDataURL(data.src)
})
}
img.src = data.src
})
.catch(() => {
})
},
onEnd() {
const { isEmpty, data } = this.$refs.signaturePad.saveSignature()
this.$emit('update:data', data)
if (!isEmpty) {
this.$refs.signaturePad.toBlob((blob) => {
this.uploadFile(blob)
})
}
},
uploadFile(blob) {
crmFileSingleSaveAPI({
file: blob,
batchId: this.value
}).then((res) => {
this.$emit('input', res.data)
}).catch(() => {})
},
handleClick(type) {
if (type === 'clear') {
this.$refs.signaturePad.clearSignature()
} else if (type === 'undo') {
this.$refs.signaturePad.undoSignature()
}
}
}
}
</script>
<style lang="scss">
.el-form-item.is-error {
.wk-signature-pad {
border-color: #f56c6c;
}
}
</style>
<style lang="scss" scoped>
.wk-signature-pad {
position: relative;
border-radius: 4px;
border: 1px solid #dcdfe6;
overflow: hidden;
&__handle {
position: absolute;
right: 15px;
bottom: 0;
.el-button--text {
color: #999;
&:hover {
color: $xr-color-primary;
}
}
}
}
</style>

@ -0,0 +1,26 @@
export const IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/svg+xml']
export const checkSaveType = type => IMAGE_TYPES.includes(type)
export const DEFAULT_OPTIONS = {
dotSize: (0.5 + 2.5) / 2,
minWidth: 0.5,
maxWidth: 2.5,
throttle: 16,
minDistance: 5,
backgroundColor: 'rgba(0,0,0,0)',
penColor: 'black',
velocityFilterWeight: 0.7,
onBegin: () => {},
onEnd: () => {}
}
export const convert2NonReactive = observerValue =>
JSON.parse(JSON.stringify(observerValue))
export const TRANSPARENT_PNG = {
src:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=',
x: 0,
y: 0
}

@ -145,8 +145,10 @@ export default {
}
})
this.$emit('input', ids)
this.$emit('change', val)
} else {
this.$emit('input', [])
this.$emit('change', [])
}
},

@ -10,7 +10,7 @@
class="slide-detail-card-container">
<el-button
v-if="showClose"
class="close-btn"
class="close-btn xr-btn--orange"
type="primary"
icon="el-icon-close"
@click="close"/>
@ -218,8 +218,8 @@ export default {
.close-btn {
position: absolute;
top: 160px;
left: -40px;
top: 153px;
left: -46px;
z-index: 1;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
@ -227,6 +227,7 @@ export default {
/deep/ i {
font-size: 26px;
margin-left: 0;
}
}
</style>

@ -1,74 +1,27 @@
<template>
<div :class="wrapper">
<template v-if="type != 'mobile'">
<el-select v-model="currentProvince" :disabled="disabled || provinceDisabled" placeholder="省" @change="getCities">
<!-- <el-option :value="placeholders.province">{{ placeholders.province }}</el-option> -->
<el-select v-model="currentProvince" :disabled="disabled || provinceDisabled" placeholder="省" @change="getCities">
<el-option
v-for="(item, index) in provinces"
:value="item.name"
:key="index"
:label="item.name"/>
</el-select>
<template v-if="!onlyProvince">
<el-select v-model="currentCity" :disabled="disabled || cityDisabled" placeholder="市" @change="getAreas">
<el-option
v-for="(item, index) in provinces"
:value="item"
v-for="(item, index) in cities"
:value="item.name"
:key="index"
:label="item"/>
:label="item.name"/>
</el-select>
<el-select v-if="!hideArea" v-model="currentArea" :disabled="disabled || areaDisabled" placeholder="区">
<el-option
v-for="(item, index) in areas "
:value="item.name"
:key="index"
:label="item.name"/>
</el-select>
<template v-if="!onlyProvince">
<el-select v-model="currentCity" :disabled="disabled || cityDisabled" placeholder="市" @change="getAreas">
<!-- <el-option :value="placeholders.city">{{ placeholders.city }}</el-option> -->
<el-option
v-for="(item, index) in cities"
:value="item"
:key="index"
:label="item"/>
</el-select>
<el-select v-if="!hideArea" v-model="currentArea" :disabled="disabled || areaDisabled" placeholder="区">
<!-- <el-option :value="placeholders.area">{{ placeholders.area }}</el-option> -->
<el-option
v-for="(item, index) in areas "
:value="item"
:key="index"
:label="item"/>
</el-select>
</template>
</template>
<template v-else>
<div :class="addressHeader">
<ul>
<li :class="{'active': tab === 1}" @click="resetProvince">{{ currentProvince && !staticPlaceholder ? currentProvince : placeholders.province }}</li>
<template v-if="!onlyProvince">
<li v-if="showCityTab" :class="{'active': tab === 2}" @click="resetCity">{{ currentCity && !staticPlaceholder ? currentCity : placeholders.city }}</li>
<li v-if="showAreaTab && !hideArea" :class="{'active': tab === 3}">{{ currentArea && !staticPlaceholder ? currentArea : placeholders.area }}</li>
</template>
</ul>
</div>
<div :class="addressContainer">
<ul v-if="tab === 1">
<li
v-for="(item, index) in provinces"
:class="{'active': item === currentProvince}"
:key="index"
@click="chooseProvince(item)">
{{ item }}
</li>
</ul>
<template v-if="!onlyProvince">
<ul v-if="tab === 2">
<li
v-for="(item, index) in cities"
:class="{'active': item === currentCity}"
:key="index"
@click="chooseCity(item)">
{{ item }}
</li>
</ul>
<ul v-if="tab === 3 && !hideArea">
<li
v-for="(item, index) in areas"
:class="{'active': item === currentArea}"
:key="index"
@click="chooseArea(item)">
{{ item }}
</li>
</ul>
</template>
</div>
</template>
</div>
</template>
@ -76,252 +29,142 @@
<script>
import DISTRICTS from './districts'
const DEFAULT_CODE = 100000
export default {
name: 'VDistpicker',
props: {
province: { type: [String, Number], default: '' },
city: { type: [String, Number], default: '' },
area: { type: [String, Number], default: '' },
type: { type: String, default: '' },
hideArea: { type: Boolean, default: false },
onlyProvince: { type: Boolean, default: false },
staticPlaceholder: { type: Boolean, default: false },
placeholders: {
type: Object,
default() {
return {
province: '',
city: '',
area: ''
}
}
},
disabled: { type: Boolean, default: false },
provinceDisabled: { type: Boolean, default: false },
cityDisabled: { type: Boolean, default: false },
areaDisabled: { type: Boolean, default: false },
addressHeader: { type: String, default: 'address-header' },
addressContainer: { type: String, default: 'address-container' },
wrapper: { type: String, default: 'distpicker-address-wrapper' }
},
data() {
return {
tab: 1,
showCityTab: false,
showAreaTab: false,
provinces: [],
cities: [],
areas: [],
currentProvince: this.determineType(this.province) || this.placeholders.province,
currentCity: this.determineType(this.city) || this.placeholders.city,
currentArea: this.determineType(this.area) || this.placeholders.area
currentProvince: '',
currentCity: '',
currentArea: ''
}
},
watch: {
currentProvince(vaule) {
this.$emit('province', this.setData(vaule))
currentProvince(value) {
this.$emit('province', this.setData(value, this.provinces))
if (this.onlyProvince) this.emit('selected')
},
currentCity(value) {
this.$emit('city', this.setData(value, this.currentProvince))
if (value != this.placeholders.city && this.hideArea) this.emit('selected')
this.$emit('city', this.setData(value, this.cities))
if (this.hideArea) this.emit('selected')
},
currentArea(value) {
this.$emit('area', this.setData(value, this.currentProvince, true))
this.$emit('area', this.setData(value, this.areas))
this.emit('selected')
},
province(value) {
this.currentProvince = this.province || this.placeholders.province
this.cities = this.determineValue(this.currentProvince, this.placeholders.province)
this.currentProvince = this.province
this.cities = this.getDataList(this.currentProvince, this.provinces)
},
city(value) {
this.currentCity = this.city || this.placeholders.city
this.areas = this.determineValue(this.currentCity, this.placeholders.city, this.currentProvince)
this.currentCity = this.city
this.areas = this.getDataList(this.currentCity, this.cities)
},
area(value) {
this.currentArea = this.area || this.placeholders.area
this.currentArea = this.area
}
},
created() {
if (this.type != 'mobile') {
this.provinces = this.getDistricts()
this.cities = this.province ? this.getDistricts(this.getAreaCode(this.determineType(this.province))) : []
this.areas = this.city ? this.getDistricts(this.getAreaCode(this.determineType(this.city), this.area)) : []
} else {
if (this.area && !this.hideArea && !this.onlyProvince) {
this.tab = 3
this.showCityTab = true
this.showAreaTab = true
this.areas = this.getDistricts(this.getAreaCode(this.determineType(this.city), this.area))
} else if (this.city && this.hideArea && !this.onlyProvince) {
this.tab = 2
this.showCityTab = true
this.cities = this.getDistricts(this.getAreaCode(this.determineType(this.province)))
} else {
this.provinces = this.getDistricts()
}
}
this.provinces = DISTRICTS
this.currentProvince = this.getNameValue(this.province, this.provinces)
this.cities = this.province ? this.getDataList(this.province, this.provinces) : []
this.currentCity = this.getNameValue(this.city, this.cities)
this.areas = this.city ? this.getDataList(this.city, this.cities) : []
this.currentArea = this.getNameValue(this.area, this.areas)
},
methods: {
setData(value, check = '', isArea = false) {
let code
if (isArea) {
code = this.getCodeByArea(value)
} else {
code = this.getAreaCode(value, check)
}
return {
code: code,
value: value
setData(value, list) {
const valueObj = list ? list.find(item => item.name === value) : null
return valueObj ? {
code: valueObj.code,
value: valueObj.name
} : {
code: '',
value: ''
}
},
getCodeByArea(value) {
let code
Object.values(this.areas).forEach((item, key) => {
if (item === value) {
code = Object.keys(this.areas)[key]
}
})
return code
},
emit(name) {
const data = {
province: this.setData(this.currentProvince)
province: this.setData(this.currentProvince, this.provinces)
}
if (!this.onlyProvince) {
this.$set(data, 'city', this.setData(this.currentCity))
this.$set(data, 'city', this.setData(this.currentCity, this.cities))
}
if (!this.onlyProvince || this.hideArea) {
this.$set(data, 'area', this.setData(this.currentArea, this.currentCity))
this.$set(data, 'area', this.setData(this.currentArea, this.areas))
}
this.$emit(name, data)
},
getCities() {
this.currentCity = this.placeholders.city
this.currentArea = this.placeholders.area
this.cities = this.determineValue(this.currentProvince, this.placeholders.province)
this.currentCity = ''
this.currentArea = ''
this.cities = this.getDataList(this.currentProvince, this.provinces)
this.cleanList('areas')
if (this.cities.length === 0) {
this.emit('selected')
this.tab = 1
this.showCityTab = false
}
},
getAreas() {
this.currentArea = this.placeholders.area
this.areas = this.determineValue(this.currentCity, this.placeholders.city, this.currentProvince)
this.currentArea = ''
this.areas = this.getDataList(this.currentCity, this.cities)
if (this.areas.length === 0) {
this.emit('selected')
this.tab = 2
this.showAreaTab = false
}
},
resetProvince() {
this.tab = 1
this.provinces = this.getDistricts()
this.showCityTab = false
this.showAreaTab = false
},
resetCity() {
this.tab = 2
this.showCityTab = true
this.showAreaTab = false
this.getCities()
},
chooseProvince(name) {
this.currentProvince = name
if (this.onlyProvince) return
this.tab = 2
this.showCityTab = true
this.showAreaTab = false
this.getCities()
},
chooseCity(name) {
this.currentCity = name
if (this.hideArea) return
this.tab = 3
this.showCityTab = true
this.showAreaTab = true
this.getAreas()
},
chooseArea(name) {
this.currentArea = name
},
getAreaCodeByPreCode(name, preCode) {
const codes = []
for (const x in DISTRICTS) {
for (const y in DISTRICTS[x]) {
if (name === DISTRICTS[x][y]) {
codes.push(y)
}
/**
* 获取数据信息
* value code/name
* list 上一级数据
*/
getDataList(value, list) {
const isCode = typeof value === 'number'
for (let index = 0; index < list.length; index++) {
const item = list[index]
if ((isCode && item.code === value) ||
!isCode && item.name === value) {
return item.children
}
}
if (codes.length > 1) {
let index
codes.forEach((item, i) => {
if (item.slice(0, 2) == preCode) {
index = i
}
})
return codes[index]
} else {
return codes[0]
}
},
getAreaCode(name, check = '') {
for (const x in DISTRICTS) {
for (const y in DISTRICTS[x]) {
if (name === DISTRICTS[x][y]) {
if (check.length > 0) {
const code = this.getAreaCodeByPreCode(check, y.slice(0, 2))
if (!code || y.slice(0, 2) !== code.slice(0, 2)) {
continue
} else {
return y
}
} else {
return y
}
}
}
}
},
getCodeValue(code) {
for (const x in DISTRICTS) {
for (const y in DISTRICTS[x]) {
if (code === parseInt(y)) {
return DISTRICTS[x][y]
/**
* 获取名称值
* value code/name
* list 当前类型数据
*/
getNameValue(value, list) {
if (typeof value === 'number') {
for (let index = 0; index < list.length; index++) {
const item = list[index]
if (item.code === value) {
return item.name
}
}
}
},
getDistricts(code = DEFAULT_CODE) {
return DISTRICTS[code] || []
},
determineValue(currentValue, placeholderValue, check = '') {
if (currentValue === placeholderValue) {
return []
} else {
return this.getDistricts(this.getAreaCode(currentValue, check))
}
},
determineType(value) {
if (typeof value === 'number') {
return this.getCodeValue(value)
}
return value
},
cleanList(name) {
this[name] = []
}
@ -337,52 +180,5 @@ export default {
width: 30%;
}
ul {
margin: 0;
padding: 0;
li {
list-style: none;
}
}
.address-header {
background-color: #fff;
ul {
display: flex;
justify-content: space-around;
align-items: stretch;
li {
display: inline-block;
padding: 10px 10px 7px;
&.active {
border-bottom: #52697f solid 3px;
color: #52697f;
}
}
}
}
.address-container {
background-color: #fff;
ul {
height: 100%;
overflow: auto;
li {
padding: 8px 10px;
border-top: 1px solid #f6f6f6;
&.active {
color: #52697f;
}
}
}
}
}
.disabled-color{
background: #f8f8f8;
}
</style>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,68 @@
<template>
<div class="wk-empty">
<div class="wk-empty__title">{{ config.emptyText || '暂无数据' }}</div>
<el-button
v-if="config.showButton"
:icon="config.buttonIcon"
class="wk-empty__button xr-btn--orange"
type="primary"
@click="btnClick">{{ config.buttonTitle || '新建' }}</el-button>
</div>
</template>
<script>
import merge from '@/utils/merge'
const DefaultEmptyProps = {
emptyText: '', //
showButton: false,
buttonIcon: 'el-icon-plus', //
buttonTitle: '' //
}
export default {
//
name: 'WkEmpty',
components: {},
props: {
props: Object
},
data() {
return {
}
},
computed: {
config() {
return merge({ ...DefaultEmptyProps }, this.props || {})
}
},
watch: {},
created() {},
mounted() {},
beforeDestroy() {},
methods: {
btnClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss">
.wk-empty {
color: #999;
&__button {
margin-top: 8px;
}
}
</style>

@ -0,0 +1,207 @@
<template>
<div class="import-history">
<div class="import-history__hd">
<span class="import-history__title">查看历史导入记录</span>
<span class="import-history__des">(错误数据只保存七天,七天后将自动失效)</span>
<el-button
class="xr-icon-close-btn"
icon="el-icon-close"
@click="closeClick" />
</div>
<el-table
:data="tableList"
class="import-history__bd"
height="250"
style="width: 100%">
<el-table-column
prop="createTime"
label="导入时间"
width="120">
<template slot-scope="scope">
{{ scope.row.createTime | createTime }}
</template>
</el-table-column>
<el-table-column
prop="realname"
label="操作人"
width="80"/>
<el-table-column
prop="address"
label="导入结果">
<template slot-scope="scope">
{{ `导入总数据${scope.row.title}条,${getImportContent(scope.row.title, scope.row.content)}` }}
</template>
</el-table-column>
<el-table-column
prop="option"
width="150"
label="操作">
<template slot-scope="scope">
<template v-if="getErrorNum(scope.row.content) > 0">
<el-button
v-if="scope.row.valid == 1"
type="text"
@click="downloadError(scope.row.messageId)">下载错误数据</el-button>
<span v-else-if="scope.row.valid == 0" class="invalid-tips">已失效</span>
</template>
</template>
</el-table-column>
</el-table>
<div class="import-history__ft">
<el-button
type="primary"
@click="closeClick">关闭</el-button>
</div>
</div>
</template>
<script>
import { systemMessageListAPI } from '@/api/common'
import {
crmDownImportErrorAPI
} from '@/api/crm/common'
import { downloadExcelWithResData } from '@/utils/index'
export default {
//
name: 'CRMImportHistory',
components: {},
filters: {
createTime(time) {
const times = time.split(' ')
return times.length > 0 ? times[0] : ''
}
},
props: {
show: {
type: Boolean,
default: false
},
// CRM
crmType: {
type: String,
default: ''
},
// moduleType
props: Object
},
data() {
return {
loading: false,
tableList: []
}
},
computed: {},
watch: {
show(value) {
if (value) {
this.getList()
}
}
},
mounted() {},
beforeDestroy() {},
methods: {
getList() {
this.loading = true
systemMessageListAPI({
page: 1,
limit: 9999,
type: {
customer: 14,
contacts: 16,
leads: 18,
product: 20,
hrm: 50
}[this.crmType] || this.props.moduleType
})
.then(res => {
this.loading = false
this.tableList = res.data.list
})
.catch(() => {
this.loading = false
})
},
/**
* 下载错误数据
*/
downloadError(messageId) {
crmDownImportErrorAPI({ messageId })
.then(res => {
downloadExcelWithResData(res)
})
.catch(() => {
})
},
getImportContent(totalSize, content) {
const list = content.split(',') || []
const updateSize = Number(list[1] || '0')
const errSize = Number(list[0] || '0')
return `覆盖${updateSize}条,导入成功${totalSize - errSize}条,导入失败${errSize}条。`
},
/**
* 获取错误数据数
*/
getErrorNum(content) {
const list = content.split(',') || []
const errSize = Number(list[0] || '0')
return parseInt(errSize)
},
/**
* 关闭
*/
closeClick() {
this.$emit('close')
}
}
}
</script>
<style lang="scss" scoped>
.import-history {
&__hd {
padding: 20px;
padding-bottom: 10px;
}
&__title {
font-size: 16px;
font-weight: 600;
color: #333;
}
&__des {
font-size: 12px;
color: #ccc;
}
&__bd {
border-top: 1px solid #e6e6e6;
}
&__ft {
padding: 10px;
background-color: #F7F8FA;
text-align: right;
}
.xr-icon-close-btn {
float: right;
}
}
.invalid-tips {
color: #999;
}
</style>

@ -0,0 +1,77 @@
import { crmQueryImportNumAPI } from '@/api/crm/common'
import Lockr from 'lockr'
export default {
data() {
return {
showCRMImport: false,
crmType: '',
crmProps: null,
crmImportStatus: '',
cacheShow: false, // 缓存展示
cacheDone: false, // 缓存导入是否完成
userInfo: null
}
},
created() {
// 处理上次缓存
const beforeImportInfo = Lockr.get('crmImportInfo')
if (beforeImportInfo && beforeImportInfo.messageId) {
this.crmType = beforeImportInfo.crmType
this.crmProps = beforeImportInfo.crmProps
this.lockrSecondQueryNum(beforeImportInfo.messageId)
this.cacheShow = true
} else {
this.cacheShow = false
}
},
computed: {
// 1.导入框展示 2.导入状态状态为空或者是等待状态 缩小框不展示
showFixImport() {
return !this.showCRMImport && this.crmImportStatus && this.crmImportStatus != 'wait'
}
},
watch: {},
methods: {
crmImportChange(status) {
this.crmImportStatus = this.showCRMImport && status == 'finish' ? '' : status
},
fixImportClick() {
this.showCRMImport = true
},
crmImportClose(status) {
if (status == 'finish') {
this.crmImportStatus = ''
}
},
/**
* 第二步查询数量
*/
lockrSecondQueryNum(messageId) {
crmQueryImportNumAPI({ messageId: messageId })
.then(res => {
if (res.data === null) { // 结束 否则 进行中
this.crmImportStatus = 'finish'
this.cacheDone = true
} else {
this.cacheDone = false
this.crmImportStatus = 'process'
}
this.showCRMImport = false
})
.catch(() => {
Lockr.rm('crmImportInfo')
})
}
}
}

@ -0,0 +1,91 @@
<template>
<flexbox class="xr-import">
<div
v-loading="loading"
v-if="loading"
class="xr-import__icon" />
<i
v-else
class="wk wk-success xr-import__icon" />
<p
:class="{ 'is-loading': loading }"
class="xr-import__label">{{ processLabel }}</p>
</flexbox>
</template>
<script>
export default {
//
name: 'XrImport',
components: {},
props: {
// wait / process / finish / error / success
processStatus: {
type: String,
default: 'wait'
}
},
data() {
return {
}
},
computed: {
loading() {
return this.processStatus == 'process'
},
processLabel() {
return this.loading ? '导入中' : '导入完成'
}
},
watch: {},
mounted() {},
beforeDestroy() {},
methods: {}
}
</script>
<style lang="scss" scoped>
.xr-import {
position: fixed;
right: 50px;
bottom: 150px;
z-index: 999999;
cursor: pointer;
width: 130px;
height: 50px;
padding: 10px;
border-radius: 25px;
background-color: white;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
&__icon {
font-size: 30px;
margin-right: 5px;
}
&__label {
font-size: 14px;
color: #333;
}
&__label.is-loading {
margin-left: 35px;
}
/deep/ .el-loading-spinner .circular {
width: 30px;
height: 30px;
}
/deep/ .el-loading-spinner {
margin-top: -15px;
}
}
.wk-success {
color: $xr-color-primary;
}
</style>

@ -0,0 +1,868 @@
<template>
<div>
<el-dialog
:visible="showCRMImport"
:title="'导入'+crmTypeName"
:append-to-body="true"
:close-on-click-modal="false"
width="750px"
@close="closeView">
<div class="dialog-body">
<el-steps
:active="stepsActive"
simple>
<el-step
v-for="(item, index) in stepList"
:key="index"
:title="item.title"
:icon="item.icon"
:status="item.status" />
</el-steps>
<div v-if="stepsActive == 1" class="step-section">
<div class="sections">
<div class="sections__title">请按照数据模板的格式准备要导入的数据<span
class="download"
@click="download">点击下载{{ crmTypeName }}导入模板</span></div>
<div class="sections__tips">导入文件请勿超过2MB约10,000条数据</div>
</div>
<div v-if="config.repeatHandleShow" class="sections">
<div class="sections__title">请选择数据重复时的处理方式{{ config.repeatRuleShow ? `(查重规则:【${ fieldUniqueInfo }】)` : '' }}</div>
<div v-if="config.repeatRuleShow" class="sections__tips">{{ crmTypeName }}{{ fieldUniqueInfo }}</div>
<div class="content">
<el-select
v-model="repeatHandling"
placeholder="请选择">
<el-option
v-for="(item, index) in [{name: '覆盖系统原有数据',value: 1},{name: '跳过',value: 2}]"
:key="index"
:label="item.name"
:value="item.value"/>
</el-select>
</div>
</div>
<div class="sections">
<div class="sections__title">{{ config.repeatHandleShow ? '三' : '二' }}请选择需要导入的文件</div>
<div class="content">
<flexbox class="file-select">
<el-input
v-model="file.name"
:disabled="true"/>
<el-button
type="primary"
@click="selectFile">选择文件</el-button>
</flexbox>
</div>
</div>
<div v-if="config.ownerSelectShow" class="sections">
<div class="sections__title">请选择负责人负责人为必填字段若不填写则会导致导入失败</div>
<div class="content">
<div class="user-cell">
<xh-user-cell
:value="user"
@value-change="userSelect"/>
</div>
</div>
</div>
<div v-if="config.poolSelectShow" class="sections">
<div class="sections__title">请选择公海</div>
<div class="content">
<div class="user-cell">
<el-select v-model="poolId" placeholder="请选择">
<el-option
v-for="item in poolList"
:key="item.poolId"
:label="item.poolName"
:value="item.poolId"/>
</el-select>
</div>
</div>
</div>
</div>
<div
v-loading="loading"
v-else-if="stepsActive == 2"
:element-loading-text="importLoadingText"
element-loading-spinner="el-icon-loading"
class="step-section">
<div class="step-section__tips">当前数据正在导入您可以点击最小化隐藏该页面数据导入不受影响</div>
</div>
<div
v-loading="loading"
v-else-if="stepsActive == 3"
class="step-section">
<div class="result-info">
<i class="wk wk-success result-info__icon" />
<p class="result-info__des">数据导入完成</p>
<p v-if="resultData" class="result-info__detail">导入总数据<span class="result-info__detail--all">{{ resultData.totalSize }}</span>条,<template v-if="resultData.updateSize">覆盖<span class="result-info__detail--suc">{{ resultData.updateSize }}</span>条,</template>导入成功<span class="result-info__detail--suc">{{ resultData.totalSize - resultData.errSize }}</span>条,导入失败<span class="result-info__detail--err">{{ resultData.errSize }}</span></p>
<el-button
v-if="resultData && resultData.errSize > 0"
class="result-info__btn--err"
type="text"
@click="downloadErrData(resultData)">下载错误数据</el-button>
</div>
</div>
<input
id="importInputFile"
type="file"
@change="uploadFile">
</div>
<span
slot="footer"
class="dialog-footer">
<el-popover
v-if="config.historyShow"
v-model="historyPopoverShow"
placement="top"
width="800"
popper-class="no-padding-popover"
trigger="click">
<import-history
:show="historyPopoverShow"
:crm-type="crmType"
:props="crmProps"
@close="historyPopoverShow = false" />
<el-button
slot="reference"
class="history-btn"
type="text">查看历史导入记录</el-button>
</el-popover>
<el-button
:class="{ 'is-hidden': !showCancel }"
@click="closeView">取消</el-button>
<el-button
v-if="sureTitle"
type="primary"
@click="sureClick">{{ sureTitle }}</el-button>
</span>
</el-dialog>
<xr-import
v-if="showFixImport"
:process-status="crmImportStatus"
@click.native="fixImportClick"/>
</div>
</template>
<script>
import {
crmQueryImportNumAPI,
crmQueryImportInfoAPI,
crmDownImportErrorAPI,
filedGetFieldAPI
} from '@/api/crm/common'
import {
crmCustomerExcelImportAPI,
crmCustomerDownloadExcelAPI,
crmCustomerPoolDownloadExcelAPI,
crmCustomerPoolNameListAPI,
crmCustomerPoolExcelImportAPI
} from '@/api/crm/customer'
import {
crmLeadsExcelImportAPI,
crmLeadsDownloadExcelAPI
} from '@/api/crm/leads'
import {
crmContactsExcelImportAPI,
crmContactsDownloadExcelAPI
} from '@/api/crm/contacts'
import {
crmProductExcelImportAPI,
crmProductDownloadExcelAPI
} from '@/api/crm/product'
import { XhUserCell } from '@/components/CreateCom'
import ImportHistory from './ImportHistory'
import XrImport from './XrImport'
// import { mapGetters } from 'vuex'
import crmTypeModel from '@/views/crm/model/crmTypeModel'
import { downloadExcelWithResData, verifyFileTypeWithFileName } from '@/utils'
import Lockr from 'lockr'
import merge from '@/utils/merge'
import ImportMixins from './ImportMixins'
const DefaultProps = {
typeName: '', //
ownerSelectShow: false,
poolSelectShow: false, //
historyShow: true,
repeatHandleShow: true,
repeatRuleShow: true, //
importRequest: null, //
importParams: null, //
templateRequest: null, //
templateParams: null, //
noImportProcess: false, //
downloadErrFuc: null, //
userInfo: null //
}
export default {
name: 'WkCRMImport', //
components: {
XhUserCell,
ImportHistory,
XrImport
},
mixins: [ImportMixins],
props: {
// show: {
// type: Boolean,
// default: false
// }
// // CRM
// crmType: {
// type: String,
// default: ''
// },
// props: {
// type: Object,
// default: () => {
// return {}
// }
// },
//
// cacheShow: {
// type: Boolean,
// default: false
// },
// cacheDone: {
// type: Boolean,
// default: false
// }
},
data() {
return {
loading: false,
fieldList: [],
repeatHandling: 1, // 1 2
file: { name: '' },
user: [],
//
poolId: '',
poolList: [],
stepsActive: 1,
stepList: [
{
icon: 'wk wk-upload',
title: '上传文件',
status: 'wait'
},
{
icon: 'wk wk-data-import',
title: '导入数据',
status: 'wait'
},
{
icon: 'wk wk-success',
title: '导入完成',
status: 'wait'
}
],
resultData: null,
processData: {
count: 0,
status: ''
},
messageId: null,
intervalTimer: null,
historyPopoverShow: false
}
},
computed: {
// ...mapGetters(['userInfo']),
crmTypeName() {
if (this.crmProps && this.crmProps.typeName) {
return this.crmProps.typeName
}
return (
{
customer: '客户',
leads: '线索',
contacts: '联系人',
product: '产品'
}[this.crmType] || ''
)
},
importLoadingText() {
if (this.processData.count) {
return `数据导入中(当前已导入${this.processData.count}条)`
}
return '数据导入中'
},
sureTitle() {
return {
1: '立即导入',
2: '最小化',
3: '确定'
}[this.stepsActive]
},
showCancel() {
return this.stepsActive != 2
},
fieldUniqueInfo() {
const uniqueList = this.fieldList.filter(item => item.isUnique == 1)
return uniqueList.map(item => item.name).join('/') || '无'
},
config() {
return merge({ ...DefaultProps }, this.crmProps || {})
}
},
watch: {
cacheShow: {
handler(val) {
//
if (val) {
// this.$emit('update:cacheShow', false)
this.cacheShow = false
const beforeImportInfo = Lockr.get('crmImportInfo')
if (beforeImportInfo && beforeImportInfo.messageId) {
this.loading = true
this.messageId = beforeImportInfo.messageId
this.stepList[0].status = 'finish'
if (this.cacheDone) {
this.stepList[1].status = 'finish'
this.stepsActive = 3
this.thirdQueryResult()
} else {
this.stepsActive = 2
this.stepList[1].status = 'process'
}
this.loopSecondQueryNum()
}
}
},
immediate: true
},
showCRMImport: {
handler(val) {
if (val) {
//
if (this.stepsActive == 1) {
if (this.config.userInfo) {
this.user = [this.config.userInfo]
}
if (this.config.poolSelectShow) {
this.getPoolList()
}
if (this.config.repeatRuleShow && this.config.repeatHandleShow) {
this.getField()
}
}
} else {
if (this.stepsActive == 3) {
this.resetData()
}
this.fieldList = []
}
},
immediate: true
},
stepsActive() {
// this.$emit('status', {
// 1: 'wait',
// 2: 'process',
// 3: 'finish'
// }[this.stepsActive])
this.crmImportChange({
1: 'wait',
2: 'process',
3: 'finish'
}[this.stepsActive])
}
// file() {
// this.getFirstStepStatus()
// },
// user() {
// this.getFirstStepStatus()
// }
},
created() {},
methods: {
import(crmType, props) {
if (this.crmType != crmType && this.showFixImport) {
this.$message.error('请先处理当前导入的数据')
} else {
this.crmType = crmType
this.crmProps = props
this.showCRMImport = true
}
},
sureClick() {
if (this.stepsActive == 1) {
if (this.stepList[0].status == 'finish') {
this.stepList[1].status = 'process'
this.stepsActive = 2
this.firstUpdateFile(res => {
const resData = res.data || {}
if (this.config.noImportProcess) {
this.loading = false
this.stepList[1].status = 'finish'
this.stepsActive = 3
this.$emit('status', 'finish')
this.resultData = resData
if (resData.errSize > 0) {
this.stepList[2].status = 'error'
} else {
this.stepList[2].status = 'finish'
}
} else {
this.messageId = resData
//
Lockr.set('crmImportInfo', {
messageId: this.messageId,
crmProps: this.config,
crmType: this.crmType
})
this.loopSecondQueryNum()
}
})
} else {
if (!this.file.name) {
this.$message.error('请选择导入文件')
} else if (
this.config.ownerSelectShow &&
(!this.user || this.user.length == 0)
) {
this.$message.error('请选择负责人')
}
}
} else {
this.closeView()
}
},
/**
* 第一步上传
*/
firstUpdateFile(result) {
let params = {}
params.repeatHandling = this.repeatHandling
params.file = this.file
if (this.config.ownerSelectShow) {
params.ownerUserId = this.user.length > 0 ? this.user[0].userId : ''
}
if (this.config.poolSelectShow) {
params.poolId = this.poolId
}
const request = this.config.importRequest || {
customer: this.config.poolSelectShow ? crmCustomerPoolExcelImportAPI : crmCustomerExcelImportAPI,
leads: crmLeadsExcelImportAPI,
contacts: crmContactsExcelImportAPI,
product: crmProductExcelImportAPI
}[this.crmType]
this.loading = true
if (this.config.importParams) {
params = {
...params,
...this.config.importParams
}
}
request(params)
.then(res => {
if (result) {
result(res)
}
})
.catch(() => {
if (result) {
result(false)
}
this.loading = false
})
},
/**
* 第二步查询数量
*/
loopSecondQueryNum() {
this.secondQueryNum()
this.intervalTimer = setInterval(() => {
if (this.processData.status == 'end') {
clearInterval(this.intervalTimer)
this.intervalTimer = null
this.thirdQueryResult()
} else {
this.secondQueryNum()
}
}, 2000)
},
secondQueryNum() {
crmQueryImportNumAPI({ messageId: this.messageId })
.then(res => {
if (res.data === null) {
this.processData.status = 'end'
} else {
this.processData.status = ''
this.processData.count = res.data
}
})
.catch(() => {
// this.processData.status = 'err'
})
},
/**
* 第三部 查询结果
*/
thirdQueryResult() {
crmQueryImportInfoAPI({ messageId: this.messageId })
.then(res => {
this.loading = false
this.stepList[1].status = 'finish'
this.stepsActive = 3
this.$emit('status', 'finish')
if (res) {
this.resultData = res.data
if (res.data.errSize > 0) {
this.stepList[2].status = 'error'
} else {
this.stepList[2].status = 'finish'
}
}
})
.catch(() => {})
},
/**
* 下载错误模板
*/
downloadErrData(result) {
this.loading = true
if (this.config.downloadErrFuc) {
this.config.downloadErrFuc(result).then(res => {
downloadExcelWithResData(res)
this.loading = false
})
.catch(() => {
this.loading = false
})
} else {
crmDownImportErrorAPI({ messageId: this.messageId })
.then(res => {
downloadExcelWithResData(res)
this.loading = false
})
.catch(() => {
this.loading = false
})
}
},
//
download() {
const request = this.config.templateRequest || {
customer: this.config.poolSelectShow ? crmCustomerPoolDownloadExcelAPI : crmCustomerDownloadExcelAPI,
leads: crmLeadsDownloadExcelAPI,
contacts: crmContactsDownloadExcelAPI,
product: crmProductDownloadExcelAPI
}[this.crmType]
request(this.config.templateParams || {})
.then(res => {
downloadExcelWithResData(res)
})
.catch(() => {})
},
//
selectFile() {
document.getElementById('importInputFile').click()
},
/** 图片选择出发 */
uploadFile(event) {
var files = event.target.files
const file = files[0]
if (verifyFileTypeWithFileName(file.name)) {
this.file = file
//
this.getFirstStepStatus()
}
event.target.value = ''
},
//
userSelect(data) {
if (data.value && data.value.length > 0) {
this.user = data.value
} else {
this.user = []
}
//
this.getFirstStepStatus()
},
getFirstStepStatus() {
//
const hasFile = this.file && this.file.size
const hasUser = this.user && this.user.length > 0
if (this.config.ownerSelectShow) {
this.stepList[0].status = hasFile && hasUser ? 'finish' : 'wait'
} else {
this.stepList[0].status = hasFile ? 'finish' : 'wait'
}
},
/**
* 公海数据
*/
getPoolList() {
crmCustomerPoolNameListAPI()
.then(res => {
this.poolList = res.data || []
this.poolId = this.poolList.length > 0 ? this.poolList[0].poolId : ''
})
.catch(() => {
})
},
//
closeView() {
// this.$emit('update:show', false)
this.showCRMImport = false
if (this.stepsActive == 3) {
this.$bus.emit('import-crm-done-bus', this.crmType)
Lockr.rm('crmImportInfo')
}
// this.$emit('close', this.stepsActive == 3 ? 'finish' : '')
this.crmImportClose(this.stepsActive == 3 ? 'finish' : '')
},
/**
* 重置页面数据
*/
resetData() {
this.repeatHandling = 1
this.file = { name: '' }
this.user = []
this.stepsActive = 1
this.stepList = [
{
icon: 'wk wk-upload',
title: '上传文件',
status: 'wait'
},
{
icon: 'wk wk-data-import',
title: '导入数据',
status: 'wait'
},
{
icon: 'wk wk-success',
title: '导入完成',
status: 'wait'
}
]
this.resultData = null
this.processData = {
count: 0,
status: ''
}
this.messageId = null
},
/**
* 获取验证字段
*/
getField() {
var params = {
label: crmTypeModel[this.crmType],
type: 1
}
filedGetFieldAPI(params)
.then(res => {
this.fieldList = res.data
})
.catch(() => {
})
}
}
}
</script>
<style scoped lang="scss">
.el-steps {
margin-bottom: 15px;
/deep/ .el-step__title {
font-size: 14px;
}
/deep/ .el-step.is-simple .el-step__arrow::before,
/deep/ .el-step.is-simple .el-step__arrow::after {
height: 10px;
width: 2px;
}
/deep/ .el-step.is-simple .el-step__arrow::after {
transform: rotate(45deg) translateY(3px);
}
/deep/ .el-step.is-simple .el-step__arrow::before {
transform: rotate(-45deg) translateY(-2px);
}
}
.step-section {
min-height: 300px;
position: relative;
/deep/ .el-loading-spinner {
top: 45%;
.el-icon-loading {
font-size: 40px;
color: #999;
}
.el-loading-text {
color: #333;
margin: 8px 0;
}
}
&__tips {
color: #999;
font-size: 12px;
position: absolute;
left: 0;
bottom: 0;
right: 0;
text-align: center;
z-index: 3000;
}
}
.sections {
font-size: 14px;
color: #333;
&__title {
font-weight: 600;
}
&__tips {
padding-left: 30px;
margin: 8px 0 15px;
color: #999;
font-size: 12px;
line-height: 1.4;
}
.download {
cursor: pointer;
color: #2362FB;
}
}
.sections__tips + .content {
padding-top: 0;
}
.content {
padding: 10px 10px 10px 30px;
.el-select {
width: 400px;
}
.user-cell {
width: 400px;
}
}
#importInputFile {
display: none;
}
.file-select {
.el-input {
width: 400px;
}
button {
margin-left: 20px;
}
}
.is-hidden {
visibility: hidden;
}
.history-btn {
float: left;
margin-left: 15px;
}
//
.result-info {
text-align: center;
padding-top: 80px;
&__icon {
font-size: 40px;
color: $xr-color-primary;
}
&__des {
margin-top: 15px;
color: #333;
font-size: 14px;
}
&__detail {
margin-top: 15px;
font-size: 12px;
color: #666;
&--all {
color: #333;
font-weight: 600;
}
&--suc {
color: $xr-color-primary;
font-weight: 600;
}
&--err {
color: #f94e4e;
font-weight: 600;
}
}
&__btn--err {
margin-top: 10px;
}
}
</style>

@ -0,0 +1,15 @@
import WkImportVue from './index.vue'
const WkImport = {}
WkImport.install = (Vue) => {
const WkCRMImportConstructor = Vue.extend(WkImportVue)
const instance = new WkCRMImportConstructor({
el: document.createElement('div')
})
document.body.appendChild(instance.$el)
Vue.prototype.$wkImport = instance
}
export default WkImport

@ -3,6 +3,7 @@
:class="{'is-select':select}"
class="xr-menu-item">
<i
:style="{backgroundColor: select ? iconColor || '#2362FB' : '#edf2f6'}"
:class="['xr-menu-item__icon', iconClass]" />
<span class="xr-menu-item__label">{{ label }}</span>
<el-badge

@ -34,13 +34,23 @@ export default {
data() {
return {
message: `新增:
1客户管理模块字段授权
2客户管理模块打印模板
3客户管理多公海模块
4数据操作日志
5系统操作日志
6登录日志
7员工管理增加批量重设部门的功能`
1增加多种自定义字段类型支持自定义字段占比配置及布局调整
2客户管理新增团队成员有效时间优化团队成员权限联系人和回款模块增加团队成员功能增加相关团队字段支持通过相关团队对团队成员进行筛选
3新增日志点赞互动功能
4新增发票模块自定义字段发票导出功能
5增加市场活动自定义表单
6跟进记录增加导入导出功能
7导入数据时增加负责人字段
8角色权限系统管理角色新增权限"角色权限查看"控制在新建员工选择角色和编辑员工角色时可查看和选择角色的范围
优化
1优化客户管理仪表盘图表展示和统计数据等
2优化导出支持操作一万条以上数据
3高级筛选判断符优化调整时间筛选增加固定时间段例如今日本月本年等
4权限优化
修复
1修复其他已知bug`
}
},
computed: {},

@ -3,7 +3,7 @@ const getLocationOrigin = () => {
}
const companyName = '悟空CRM'
const version = 'V11.0.3'
const version = 'V11.1.0'
const baiduKey = 'lcuOQ71SCZhqpxsr1vL2mXoplWEoVctL'
export default {

@ -68,6 +68,9 @@ Vue.use(FileUpload)
import WkFileSelect from '@/components/NewCom/WkFile/Select/main.js'
Vue.use(WkFileSelect)
import WkImport from '@/components/WkImport/main.js'
Vue.use(WkImport)
/** 懒加载图片 */
import VueSrc from './directives/src'
Vue.directive('src', VueSrc)

@ -4,97 +4,119 @@ export default {
* 根据类型获取条件
* @param {*} formType
*/
getAdvancedFilterOptions(formType) {
if (
formType == 'check_status' ||
formType == 'deal_status'
) {
getAdvancedFilterOptions(formType, fieldName) {
// 单行文本、多行文本、网址、手机、邮箱
// 等于、不等于、包含、不包含、开始于、结束于、为空、不为空
if (formType == 'text' ||
formType == 'textarea' ||
formType == 'website' ||
formType == 'mobile' ||
formType == 'email' ||
formType == 'module') {
return [
{ value: 'is', label: '等于', disabled: false, type: 1 },
{ value: 'isNot', label: '不等于', disabled: false, type: 2 }
{ value: 'isNot', label: '不等于', disabled: false, type: 2 },
{ value: 'contains', label: '包含', disabled: false, type: 3 },
{ value: 'notContains', label: '不包含', disabled: false, type: 4 },
{ value: 'startWith', label: '开始于', disabled: false, type: 12 },
{ value: 'endWith', label: '结束于', disabled: false, type: 13 },
{ value: 'isNull', label: '为空', disabled: false, type: 5 },
{ value: 'isNotNull', label: '不为空', disabled: false, type: 6 }
]
} else if (
formType == 'user' ||
formType == 'single_user' ||
formType == 'structure'
fieldName === 'invoice_status' ||
formType == 'check_status' ||
formType == 'deal_status'
) {
return [
// { value: 'contains', label: '包含', disabled: false, type: 3 },
// { value: 'notContains', label: '不包含', disabled: false, type: 4 }
{ value: 'is', label: '等于', disabled: false, type: 1 },
{ value: 'isNot', label: '不等于', disabled: false, type: 2 }
]
} else if (
// 下拉框 等于、不等于、为空、不为空
formType == 'select'
) {
return [
{ value: 'in', label: '等于', disabled: false, type: 1 },
{ value: 'isNot', label: '不等于', disabled: false, type: 2 }
{ value: 'is', label: '等于', disabled: false, type: 1 },
{ value: 'isNot', label: '不等于', disabled: false, type: 2 },
{ value: 'isNull', label: '为空', disabled: false, type: 5 },
{ value: 'isNotNull', label: '不为空', disabled: false, type: 6 }
]
} else if (
formType == 'checkbox'
// 布尔值 等于、不等于
formType == 'boolean_value'
) {
return [
{ value: 'is', label: '等于', disabled: false, type: 1 },
{ value: 'contains', label: '包含', disabled: false, type: 3 }
{ value: 'isNot', label: '不等于', disabled: false, type: 2 }
]
} else if (
formType == 'module' ||
formType == 'text' ||
formType == 'textarea'
formType == 'checkbox' ||
formType == 'location'
) {
return [
{ value: 'is', label: '等于', disabled: false, type: 1 },
{ value: 'isNot', label: '不等于', disabled: false, type: 2 },
{ value: 'contains', label: '包含', disabled: false, type: 3 },
{ value: 'notContains', label: '不包含', disabled: false, type: 4 }
{ value: 'notContains', label: '不包含', disabled: false, type: 4 },
{ value: 'isNull', label: '为空', disabled: false, type: 5 },
{ value: 'isNotNull', label: '不为空', disabled: false, type: 6 }
]
} else if (formType == 'floatnumber' || formType == 'number') {
// 数字、货币、百分数
// 等于、不等于、大于、大于等于、小于、小于等于、区间(范围)、为空、不为空
} else if (
formType == 'number' ||
formType == 'floatnumber' ||
formType == 'percent'
) {
return [
{ value: 'is', label: '等于', disabled: false, type: 1 },
{ value: 'isNot', label: '不等于', disabled: false, type: 2 },
{ value: 'contains', label: '包含', disabled: false, type: 3 },
{ value: 'notContains', label: '不包含', disabled: false, type: 4 },
{ value: 'isNull', label: '为空', disabled: false, type: 5 },
{ value: 'isNotNull', label: '不为空', disabled: false, type: 6 },
{ value: 'gt', label: '大于', disabled: false, type: 7 },
{ value: 'egt', label: '大于等于', disabled: false, type: 8 },
{ value: 'lt', label: '小于', disabled: false, type: 9 },
{ value: 'elt', label: '小于等于', disabled: false, type: 10 }
]
} else if (formType == 'category') {
return [
{ value: 'is', label: '等于', disabled: false, type: 1 },
{ value: 'isNot', label: '不等于', disabled: false, type: 2 },
{ value: 'contains', label: '包含', disabled: false, type: 3 },
{ value: 'not_contain', label: '不包含', disabled: false, type: 4 }
{ value: 'elt', label: '小于等于', disabled: false, type: 10 },
{ value: 'range', label: '等于(范围)', disabled: false, type: 14 },
{ value: 'isNull', label: '为空', disabled: false, type: 5 },
{ value: 'isNotNull', label: '不为空', disabled: false, type: 6 }
]
} else if (formType == 'mobile' || formType == 'email') {
// 日期、日期时间
// 等于、不等于、早于(大于)、晚于(小于)、不早于(小于等于)、不晚于(大于等于)、等于(时间段)、为空、不为空
} else if (
formType == 'date' ||
formType == 'datetime'
) {
return [
{ value: 'is', label: '等于', disabled: false, type: 1 },
{ value: 'isNot', label: '不等于', disabled: false, type: 2 },
{ value: 'contains', label: '包含', disabled: false, type: 3 },
{ value: 'notContains', label: '不包含', disabled: false, type: 4 },
{ value: 'startWith', label: '开始于', disabled: false, type: 8 },
{ value: 'endWith', label: '结束于', disabled: false, type: 10 },
{ value: 'lt', label: '早于', disabled: false, type: 9 },
{ value: 'gt', label: '晚于', disabled: false, type: 7 },
{ value: 'egt', label: '不早于', disabled: false, type: 8 },
{ value: 'elt', label: '不晚于', disabled: false, type: 10 },
{ value: 'range', label: '等于(时间段)', disabled: false, type: 14 },
{ value: 'isNull', label: '为空', disabled: false, type: 5 },
{ value: 'isNotNull', label: '不为空', disabled: false, type: 6 }
]
} else {
// 日期区间 地址 无判断符
} else if (
formType == 'date_interval' ||
formType == 'position'
) {
return []
// 人员、部门 包含、不包含、为空、不为空
} else if (
formType == 'user' ||
formType == 'structure' ||
formType == 'single_user'
) {
return [
{ value: 'is', label: '等于', disabled: false, type: 1 },
{ value: 'isNot', label: '不等于', disabled: false, type: 2 },
{ value: 'contains', label: '包含', disabled: false, type: 3 },
{ value: 'notContains', label: '不包含', disabled: false, type: 4 },
{ value: 'startWith', label: '开始于', disabled: false, type: 8 },
{ value: 'endWith', label: '结束于', disabled: false, type: 10 },
{ value: 'isNull', label: '为空', disabled: false, type: 5 },
{ value: 'isNotNull', label: '不为空', disabled: false, type: 6 },
{ value: 'gt', label: '大于', disabled: false, type: 7 },
{ value: 'egt', label: '大于等于', disabled: false, type: 8 },
{ value: 'lt', label: '小于', disabled: false, type: 9 },
{ value: 'elt', label: '小于等于', disabled: false, type: 10 }
{ value: 'isNotNull', label: '不为空', disabled: false, type: 6 }
]
} else {
return []
}
}
}

@ -170,7 +170,7 @@ export default {
} else if (status == 2) {
return '通过'
} else if (status == 3) {
return '失败'
return '拒绝'
} else if (status == 1) {
return '审核中'
} else if (status == 4) {

@ -17,10 +17,10 @@ export default {
*/
getItemValue(item, detail, type) {
detail = detail || {}
if ((item.formType || item.form_type) == 'contacts' ||
(item.formType || item.form_type) == 'customer' ||
(item.formType || item.form_type) == 'contract' ||
(item.formType || item.form_type) == 'business'
if ((item.form_type || item.form_type) == 'contacts' ||
(item.form_type || item.form_type) == 'customer' ||
(item.form_type || item.form_type) == 'contract' ||
(item.form_type || item.form_type) == 'business'
) {
// crm相关信息特殊处理
if (type === 'update') {
@ -58,9 +58,9 @@ export default {
} else if (item.form_type == 'user' ||
item.form_type == 'structure' ||
item.form_type == 'file' ||
item.formType == 'user' ||
item.formType == 'structure' ||
item.formType == 'file'
item.form_type == 'user' ||
item.form_type == 'structure' ||
item.form_type == 'file'
) {
if (type === 'update') {
return item.value ? objDeepCopy(item.value) : []
@ -70,10 +70,44 @@ export default {
: []
}
} else {
if (type == 'update') {
return item.value || ''
if (
item.form_type == 'number' ||
item.form_type == 'floatnumber' ||
item.form_type == 'percent' ||
item.form_type == 'number' ||
item.form_type == 'floatnumber' ||
item.form_type == 'percent'
) {
if (type == 'update') {
return isEmpty(item.value) ? undefined : item.value
} else {
return isEmpty(item.default_value) ? undefined : item.default_value
}
} else if (item.form_type == 'detail_table' || item.form_type == 'detail_table') {
const baseFieldList = item.value || [item.fieldExtendList]
// 二维数组
const tableValue = []
baseFieldList.forEach(bigItem => {
const subForm = {}
tableValue.push(subForm)
bigItem.forEach(subItem => {
// 未转换 取 field 值
if ([
'select',
'checkbox'
].includes(subItem.form_type)) {
subItem.optionsData = null
}
subForm[subItem.field] = this.getItemValue(subItem, null, type)
})
})
return tableValue
} else {
return item.default_value || ''
if (type == 'update') {
return item.value || ''
} else {
return item.default_value || ''
}
}
}
},
@ -83,42 +117,49 @@ export default {
*/
getUniquePromise(field, value, detail) {
return new Promise((resolve, reject) => {
// var validatesParams = {}
// validatesParams.field_id = field.field_id
// if (isArray(value)) {
// let postValue = ''
// if (value.length > 0) {
// if (
// field.form_type == 'user' ||
// field.form_type == 'structure'
// ) {
// postValue = value
// .map(valueItem => {
// return field.form_type == 'user'
// ? valueItem.userId
// : valueItem.id
// })
// .join(',')
// } else if (field.field == 'categoryId') {
// if (value && value.length) {
// postValue = value[value.length - 1]
// } else {
// postValue = ''
// }
// } else if (field.form_type == 'checkbox') {
// postValue = value.join(',')
// }
// }
// validatesParams.val = postValue
// } else {
// validatesParams.val = value
// validatesParams.types = field.types
// validatesParams.field = field.field
// }
// if (detail.type == 'update') {
// validatesParams.id = detail.action_id || detail.id
// }
var validatesParams = {}
validatesParams.fieldId = field.fieldId
if (isArray(value)) {
let postValue = ''
if (value.length > 0) {
if (
field.formType == 'user' ||
field.formType == 'structure'
) {
postValue = value
.map(valueItem => {
return field.formType == 'user'
? valueItem.userId
: valueItem.id
})
.join(',')
} else if (field.fieldName == 'categoryId') {
if (value && value.length) {
postValue = value[value.length - 1]
} else {
postValue = ''
}
} else if (field.formType == 'checkbox') {
postValue = value.join(',')
}
}
validatesParams.val = postValue
} else {
validatesParams.val = value
validatesParams.types = field.types
validatesParams.field = field.field
}
validatesParams.field_id = field.field_id
validatesParams.val = this.getRealParams(field, value)
validatesParams.types = field.types
validatesParams.field = field.field
if (detail.type == 'update') {
validatesParams.id = detail.action_id || detail.id
}
filedValidatesAPI(validatesParams).then(res => {
// status 1 通过 0
const resData = res || {}
@ -138,7 +179,109 @@ export default {
})
})
},
/**
* 获取字段默认内容
*/
getFormItemDefaultProperty(item, isCopy = true) {
const temp = isCopy ? objDeepCopy(item) : item
temp.field = item.field
temp.field = item.field
// 细化 precisions
if ((item.form_type || item.form_type) === 'date_interval') {
// 1 日期 2 时间日期
if (item.precisions === 2) {
temp.dateType = 'datetimerange'
temp.dateValueFormat = 'yyyy-MM-dd HH:mm:ss'
} else {
temp.dateType = 'daterange'
temp.dateValueFormat = 'yyyy-MM-dd'
}
} else if ((item.form_type || item.form_type) === 'position') {
// 1 省/地区、市、区/县、详细地址 2 省/地区、市、区/县
// 3 省/地区、市 4 省/地区
temp.showDetail = item.precisions === 1
temp.hideArea = item.precisions === 3 || item.precisions === 4
temp.onlyProvince = item.precisions === 4
} else if ((item.form_type || item.form_type) === 'detail_table') {
// 校准默认单元数据
// authLevel 1 不能查看不能编辑 2可查看 3 可编辑可查看
const fieldForm = {}
temp.fieldExtendList.forEach(extendItem => {
const copyExtendItem = objDeepCopy(extendItem)
this.getFormItemDefaultProperty(extendItem, false)
extendItem.show = extendItem.is_hidden !== 1
if (extendItem.show) {
extendItem.rules = this.getRules(copyExtendItem)
}
this.getItemRadio(extendItem, extendItem)
fieldForm[extendItem.field] = this.getItemValue(extendItem)
})
temp.fieldForm = fieldForm // 用于追加新事项
const fieldExtendList = objDeepCopy(item.fieldExtendList)
const baseFieldList = isEmpty(item.value) ? [fieldExtendList] : item.value
// 二维数组
const tableFieldList = []
baseFieldList.forEach(bigItem => {
const subValue = []
bigItem.forEach(subItem => {
const copySubItem = objDeepCopy(subItem)
const subTemp = this.getFormItemDefaultProperty(subItem, false)
// 特殊字段允许多选
this.getItemRadio(subItem, subTemp)
subTemp.show = subTemp.is_hidden !== 1
if (subTemp.show) {
subTemp.rules = this.getRules(copySubItem)
}
subValue.push(subTemp)
})
tableFieldList.push(subValue)
})
temp.fieldList = tableFieldList
}
return temp
},
/**
* 获取二维数组字段
* @param {*} array
* @param {*} form_type
*/
getItemWithFromType(array, form_type) {
let item = null
for (let index = 0; index < array.length; index++) {
const children = array[index]
for (let childIndex = 0; childIndex < children.length; childIndex++) {
const element = children[childIndex]
if (element.form_type === form_type) {
item = element
break
}
}
if (item) {
break
}
}
return item
},
/**
* 循环二维数组
* @param {*} array
* @param {*} result
*/
itemsForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
const children = array[index]
for (let childIndex = 0; childIndex < children.length; childIndex++) {
const element = children[childIndex]
callback(element, index, childIndex, children)
}
}
},
/**
* 获取字段是否可编辑
*/
@ -153,9 +296,9 @@ export default {
* user single_user structure
*/
getItemRadio(field, data) {
if (field.formType == 'single_user') {
if (field.form_type == 'single_user') {
data.radio = true
} else if (field.formType == 'user' || field.formType == 'structure' || field.form_type == 'user' || field.form_type == 'structure') {
} else if (field.form_type == 'user' || field.form_type == 'structure' || field.form_type == 'user' || field.form_type == 'structure') {
data.radio = false
}
},
@ -188,7 +331,8 @@ export default {
if (this.action.type == 'relative') {
crmItem = this.action.data[fromType]
} else {
const crmObj = list.find(listItem => {
const childIsArray = list.length > 0 && isArray(list[0])
const crmObj = childIsArray ? this.getItemWithFromType(list, fromType) : list.find(listItem => {
return listItem.form_type === fromType
})
if (crmObj && crmObj.value && crmObj.value.length > 0) {
@ -202,20 +346,39 @@ export default {
* 获取提交参数
*/
getSubmiteParams(array, data) {
var params = { address: [] }
// var params = { address: [] }
var params = {}
for (let index = 0; index < array.length; index++) {
const field = array[index]
let dataValue = null
if (field.hasOwnProperty('show')) {
dataValue = field.show ? data[field.field] : null
} else {
dataValue = data[field.field]
}
if (field.form_type == 'product') {
this.getProductParams(params, data[field.field])
this.getProductParams(params, dataValue)
} else if (field.form_type == 'map_address') {
this.getCustomerAddressParams(params, data[field.field])
this.getCustomerAddressParams(params, dataValue)
} else if (field.field_type == 1) {
const fieldValue = this.getRealParams(field, data[field.field])
const fieldValue = this.getRealParams(field, dataValue)
params[field.field] = isEmpty(fieldValue) ? '' : fieldValue
} else {
field.value = this.getRealParams(field, data[field.field], data)
} else if (field.form_type !== 'desc_text') { // 描述文字忽略
field.value = this.getRealParams(field, dataValue, data)
params[field.field] = field.value
// params.field.push({
// field: field.field,
// field_type: field.field_type,
// name: field.name,
// type: field.type,
// field_id: field.field_id,
// value: this.getRealParams(field, dataValue)
// })
}
// else {
// field.value = this.getRealParams(field, dataValue, data)
// params[field.field] = field.value
// }
}
return params
},
@ -290,10 +453,10 @@ export default {
}
return ''
} else if (field.form_type == 'checkbox') {
if (dataValue && dataValue.length > 0) {
if (isArray(dataValue)) {
return dataValue.join(',')
}
return ''
return dataValue
} else if (field.form_type == 'date') {
if (dataValue) {
return dataValue
@ -311,9 +474,178 @@ export default {
return dataValue
}
return ''
} else if (field.form_type == 'detail_table') {
const fieldExtendList = field.fieldExtendList || []
const values = []
const tableValues = Array.isArray(dataValue) ? dataValue : []
for (let tableValueIndex = 0; tableValueIndex < tableValues.length; tableValueIndex++) {
const tableValue = tableValues[tableValueIndex]
// 去除空值数据
if (!this.getFormValueIsEmpty(fieldExtendList, tableValue)) {
const valuesItems = []
fieldExtendList.forEach(tableField => {
const copyTableField = objDeepCopy(tableField)
delete copyTableField.companyId
copyTableField.value = this.getRealParams(copyTableField, tableValue[copyTableField.field])
valuesItems.push(copyTableField)
})
values.push(valuesItems)
}
}
return values
}
return dataValue
},
/**
* 判断对象值是否是空
*/
getFormValueIsEmpty(fieldList, valueObj) {
for (let index = 0; index < fieldList.length; index++) {
const field = fieldList[index]
const value = valueObj[field.field]
if (field.form_type === 'select' || field.form_type === 'checkbox') {
if (isObject(value) && !isEmpty(value.select)) {
return false
}
} else if (field.form_type === 'location') {
if (isObject(value) && (!isEmpty(value.lat) || !isEmpty(value.lng) || !isEmpty(value.address))) {
return false
}
} else if (!isEmpty(value)) {
return false
}
}
return true
},
/**
* 获取逻辑表单隐藏id
*/
getFormAssistIds(list, valueObj) {
let allIds = []
list.forEach(items => {
items.forEach(item => {
if ([
'select',
'checkbox'
].includes((item.form_type || item.form_type)) &&
item.remark === 'options_type' &&
item.optionsData) {
for (const key in item.optionsData) {
allIds = allIds.concat(item.optionsData[key] || [])
}
}
})
})
allIds = allIds.filter(o => Boolean(o))
allIds = Array.from(new Set(allIds))
const ignoreIds = []
this.getFormAssistData(list, valueObj, allIds, ignoreIds)
return allIds.filter(o => !ignoreIds.includes(o))
},
/**
* 获取信息
*/
getFormAssistData(list, valueObj, allIds, ignoreIds) {
// let ignorIds = []
const ignoreLength = ignoreIds.length
list.forEach(items => {
items.forEach(item => {
if ([
'select',
'checkbox'
].includes((item.form_type || item.form_type)) &&
item.remark === 'options_type' &&
item.optionsData) {
let value = valueObj ? valueObj[item.field || item.field] : item.value
if (!allIds.includes(item.formAssistId)) {
if ((item.form_type || item.form_type) === 'select') {
if (isEmpty(value)) {
value = []
} else {
value = item.setting.includes(value) ? [value] : ['其他']
}
} else if ((item.form_type || item.form_type) === 'checkbox') {
if (isArray(value)) {
const copyValue = objDeepCopy(value)
const otherItem = copyValue.filter((name) => !item.setting.includes(name))
if (otherItem.length > 0) {
const newValue = copyValue.filter((name) => !otherItem.includes(name))
newValue.push('其他')
value = newValue
}
} else {
value = []
}
}
for (const key in item.optionsData) {
if (value && value.includes(key)) {
const keyValue = item.optionsData[key] || []
for (let index = 0; index < keyValue.length; index++) {
const id = keyValue[index]
if (!ignoreIds.includes(id)) {
ignoreIds.push(id)
}
}
}
}
}
}
})
})
if (ignoreLength !== ignoreIds.length) {
const newAllIds = allIds.filter(o => !ignoreIds.includes(o))
this.getFormAssistData(list, valueObj, newAllIds, ignoreIds)
}
},
/**
* 获取表单展示内容
* data 用于相关模块新建填充模块值
* type 新建编辑
*/
getFormContentByOptionsChange(fieldList, formObj, rules = {}, data = {}, type) {
const allFieldRules = this.fieldRules || rules
const actionData = this.action && this.action.data ? this.action.data : data
const actionType = this.action && this.action.type ? this.action.type : type
const fieldRules = {}
const fieldForm = {}
// 依据最新的值获取隐藏的ids
const assistIds = this.getFormAssistIds(fieldList, formObj)
this.itemsForEach(fieldList, fieldItem => {
fieldItem.show = !assistIds.includes(fieldItem.formAssistId)
// 展示 并且 允许编辑,加入验证规则
if (fieldItem.show && !fieldItem.disabled) {
if (allFieldRules[fieldItem.field]) {
fieldRules[fieldItem.field] = allFieldRules[fieldItem.field]
} else {
fieldRules[fieldItem.field] = this.getRules(fieldItem)
}
}
// 值获取
if (fieldItem.show) {
if (formObj[fieldItem.field]) {
fieldForm[fieldItem.field] = formObj[fieldItem.field]
} else {
fieldForm[fieldItem.field] = this.getItemValue(fieldItem, actionData, actionType)
}
}
})
return {
fieldForm,
fieldRules
}
}
}

@ -113,7 +113,7 @@ export default [
}, {
name: 'workbenchHandlefield',
path: 'workbench-custom-field/:type/:id/:label',
component: () => import('@/views/admin/crm/HandleField'),
component: () => import('@/views/admin/fields'),
hidden: true,
meta: {
activeMenu: '/manage/system-workbench'
@ -204,6 +204,16 @@ export default [
requiresAuth: true,
permissions: ['manage', 'crm', 'achievement']
}
}, {
name: 'customField',
path: 'custom-field/:type/:label/:id',
component: () => import('@/views/admin/fields'),
hidden: true,
meta: {
activeMenu: '/manage/customer/custom-field',
requiresAuth: true,
permissionList: [['manage', 'crm', 'field'], ['manage', 'crm', 'activityForm']]
}
}, {
name: 'handlefield',
path: 'custom-field/:type/:id/:label',

@ -18,6 +18,8 @@
}
.xr-btn--green:hover,
.xr-btn--green.is-disabled,
.xr-btn--green.is-disabled:hover,
.xr-btn--green:focus {
background: #4ca824;
border-color: #4ca824;
@ -36,6 +38,8 @@
}
.xr-btn--orange:hover,
.xr-btn--orange.is-disabled,
.xr-btn--orange.is-disabled:hover,
.xr-btn--orange:focus {
background: #fc7d63;
border-color: #fc7d63;
@ -54,6 +58,8 @@
}
.xr-btn--red:hover,
.xr-btn--red.is-disabled,
.xr-btn--red.is-disabled:hover,
.xr-btn--red:focus {
background: #fa6060;
border-color: #fa6060;

@ -1,7 +1,7 @@
[class*="sprite-"] {
width:25px;
height: 25px;
background: url('../assets/img/sprite/vue-emoji.png');
background: url('https://file.72crm.com/static/pc/images/emoji/sprite.png');
background-repeat: no-repeat;
vertical-align: top;
display: inline-block;

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 351 KiB

After

Width:  |  Height:  |  Size: 427 KiB

Binary file not shown.

Binary file not shown.

@ -9,6 +9,8 @@
@import './iconfont/iconfont.css';
@import '../directives/style.scss';
//
@import './org-tree.scss';
body {
height: 100%;
@ -165,7 +167,8 @@ div[lazy=loading] {
.router-view {
width: 100%;
position: relative;
height: 100%;
// height: 100%;
flex: 1;
overflow: hidden;
}
@ -362,3 +365,10 @@ div[lazy=loading] {
}
}
}
//
.el-picker-panel {
.el-picker-panel__sidebar {
padding-bottom: 40px;
}
}

@ -0,0 +1,30 @@
.org-tree-company {
color: #42526E;
background-color: #ECEEF2;
box-shadow: none !important;
font-weight: 600;
font-size: 12px;
}
.org-tree-department {
color: #42526E;
font-weight: 600;
box-shadow: none !important;
font-size: 12px;
border: 2px solid #42526E;
padding: 8px 15px !important;
}
.org-tree-node-label {
cursor: pointer;
}
.org-tree-node:not(:last-child):after,
.org-tree-node:not(:first-child):before {
border-top: 2px solid #42526E;
}
.org-tree-node:after,
.org-tree-node-children:before {
border-left: 2px solid #42526E;
}

@ -83,34 +83,34 @@
}
//
.side-detail__tabs--default {
/deep/ .side-detail__tabs--default {
flex: 1;
overflow: hidden;
/deep/ .el-tabs__header {
.el-tabs__header {
padding: 0 20px;
}
/deep/ .el-tabs__item {
.el-tabs__item {
color: #333;
font-size: 12px;
top: 2px;
margin-top: -2px;
}
/deep/ .el-tabs__nav-scroll {
.el-tabs__nav-scroll {
min-height: 39px;
}
/deep/ .el-tabs__item.is-active {
.el-tabs__item.is-active {
color: #333;
}
/deep/ .el-tabs {
.el-tabs {
height: calc(100% - 15px) !important;
}
/deep/ .el-tabs__content {
.el-tabs__content {
height: calc(100% - 55px) !important;
padding: 0;
overflow: hidden;
@ -118,11 +118,11 @@
.el-tab-pane {
height: 100%;
overflow: hidden;
overflow-y: auto;
}
}
/deep/ .el-tabs__nav-wrap::after {
.el-tabs__nav-wrap::after {
height: 1px;
}

@ -99,7 +99,7 @@
}
}
//
//
.el-table-header--white {
th {
border-right-width: 0;

@ -173,7 +173,7 @@ export default {
this.loading = true
adminSystemSaveAPI({
name: this.name,
logo: this.save_name
logo: this.save_name || this.logo && this.logo.split('uploads/')[1]
})
.then(res => {
this.loading = false

@ -121,7 +121,7 @@
字数上限
</div>
<el-input
v-model="field.maxLength"
v-model="field.max_length"
:maxlength="4"
:disabled="disabled"/>
<div class="input-tips"><span>*</span>上限为2000字</div>
@ -200,7 +200,7 @@ export default {
is_null: false, //
is_hidden: false, //
input_tips: '', //
maxLength: '', // textarea
max_length: '', // textarea
default_value: '', //
setting: '', // setting
showSetting: '', //

@ -40,10 +40,6 @@
type="text"
size="small"
@click="handleCustomField('edit', scope.row, scope.$index)">编辑</el-button>
<el-button
type="text"
size="small"
@click="handleCustomField('preview', scope.row, scope.$index)">预览</el-button>
</template>
</el-table-column>
</el-table>
@ -115,7 +111,7 @@ export default {
handleCustomField(type, item, index) {
if (type == 'edit') {
this.$router.push({
name: 'handlefield',
name: 'customField',
params: {
// type: {
// 1: 'crm_leads',
@ -137,7 +133,9 @@ export default {
'crm_business': 5,
'crm_contract': 6,
'crm_receivables': 7,
'crm_visit': 17
'crm_visit': 17,
'crm_invoice': 18,
'crm_receivables_plan': 19
}[item.types]
}
})
@ -167,6 +165,10 @@ export default {
return require('@/assets/img/crm/receivables.png')
} else if (types === 'crm_visit') {
return require('@/assets/img/crm/visit.png')
} else if (types === 'crm_invoice') {
return require('@/assets/img/crm/invoice.png')
} else if (types === 'crm_receivables_plan') {
return require('@/assets/img/crm/receivables_plan.png')
}
return require('@/assets/img/crm/product.png')
}

@ -0,0 +1,39 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
class="field-boolean"
@click="emitClick"
@action="handleAction">
<el-switch
v-model="field.default_value"
active-value="1"
inactive-value="0" />
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import mixins from './mixins'
export default {
name: 'FieldBoolean',
components: {
FieldWrapper
},
mixins: [mixins],
methods: {}
}
</script>
<style scoped lang="scss">
.box {
font-size: 14px;
padding: 10px 0;
border: 1px solid #e1e1e1;
border-radius: 3px;
background: white;
text-align: center;
}
</style>

@ -0,0 +1,86 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
class="field-checkbox"
@click="emitClick"
@action="handleAction">
<el-checkbox-group
v-if="field.precisions === 1"
v-model="field.default_value"
:disabled="disabled">
<el-checkbox
v-for="(item, index) in field.setting"
:key="index"
:label="item"
class="checkbox" />
</el-checkbox-group>
<div
v-else
class="select-content">
<el-select
v-model="field.default_value"
multiple
placeholder="请选择">
<el-option
v-for="(item, index) in field.setting"
:key="index"
:label="item"
:value="item" />
</el-select>
<div class="mask" />
</div>
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import mixins from './mixins'
export default {
name: 'FieldCheckbox',
components: {
FieldWrapper
},
mixins: [mixins],
watch: {
field: {
handler() {
//
if (!this.field.precisions) {
this.$set(this.field, 'precisions', 1)
}
},
deep: true,
immediate: true
}
}
}
</script>
<style scoped lang="scss">
.select-content {
position: relative;
width: 100%;
color: #333;
.mask {
position: absolute;
top: 0;
left: 0;
z-index: 100;
width: 100%;
height: 100%;
background-color: transparent;
display: block;
}
.el-select {
width: 100%;
}
}
</style>

@ -0,0 +1,56 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
class="field-date-interval"
@click="emitClick"
@action="handleAction">
<flexbox class="range-box">
<i class="el-icon-date icon" />
<flexbox-item :class="{ placeholder: !Boolean(field.default_value) }">
{{ field.default_value[0] || '开始时间' }}
</flexbox-item>
<span></span>
<flexbox-item :class="{ placeholder: !Boolean(field.default_value) }">
{{ field.default_value[1] || '结束时间' }}
</flexbox-item>
</flexbox>
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import mixins from './mixins'
export default {
name: 'FieldDateInterval',
components: {
FieldWrapper
},
mixins: [mixins],
methods: {}
}
</script>
<style scoped lang="scss">
.range-box {
width: 100%;
text-align: center;
font-size: 14px;
border: 1px solid #e1e1e1;
border-radius: 3px;
background: white;
padding: 10px;
.icon {
color: #999;
}
.placeholder {
flex: 1;
color: #999;
}
}
</style>

@ -0,0 +1,77 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
hidden-title
class="field-desc-text"
@click="emitClick"
@action="handleAction">
<tinymce
:value="field.default_value"
:disabled="true"
:toolbar="[]"
:init="{
menubar: false,
toolbar_sticky: true,
statusbar: false,
placeholder: '描述文字内容',
quickbars_selection_toolbar: false,
contextmenu: '',
content_style: ' * {color: #262626; margin: 0;} body { font-size: 14px; }',
plugins: 'autoresize',
autoresize_bottom_margin: 0
}"
class="rich-txt" />
<div class="field-desc-text-cover"/>
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import Tinymce from '@/components/Tinymce'
import mixins from './mixins'
export default {
name: 'FieldDescText',
components: {
FieldWrapper,
Tinymce
},
mixins: [mixins],
methods: {}
}
</script>
<style lang="scss">
.field-desc-text {
.tox-tinymce {
border: none;
}
.tox .tox-edit-area__iframe {
background-color: unset;
}
}
</style>
<style scoped lang="scss">
.field-desc-text {
padding: 15px 10px;
&-cover {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
cursor: move;
}
}
.rich-txt {
width: 100%;
background-color: white;
border: 0 none;
}
</style>

@ -0,0 +1,224 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
class="field-detail-table"
@click="emitClick"
@action="handleAction">
<div
:class="{'is-empty': isEmpty}"
class="box">
<draggable
:list="list"
:options="dragListConfig"
:class="{'is-table': field.precisions === 2}"
class="field-list"
@end="dragListEnd"
@add="dragAdded">
<div v-if="isEmpty" class="empty-box">
<div class="empty-box-title">可拖拽添加多个字段</div>
<div class="empty-box-desc">不支持明细中添加明细字段</div>
</div>
<template v-if="!isEmpty && field.precisions === 1">
<component
v-for="(field, index) in list"
:key="index"
:is="field | typeToComponentName"
:field="field"
:point="[index, 0]"
:active-point="[null, null]"
class="draggable-hook"
@click="emitClick" />
</template>
<template v-if="!isEmpty && field.precisions === 2">
<el-table
:data="tableData"
border
style="width: 100%">
<el-table-column
v-for="(field, index) in list"
:key="index"
:prop="field.fieldName"
:label="field.name">
<template slot-scope="scope">
<div class="input-box" />
</template>
</el-table-column>
<el-table-column label="操作" fixed="right">
<template slot-scope="scope">
<el-button>删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
</draggable>
<div v-if="!isEmpty" class="add-btn">
<el-button type="text">
<i class="wk wk-l-plus" />
{{ field.remark || '' }}
</el-button>
</div>
</div>
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import draggable from 'vuedraggable'
import mixins from './mixins'
import { isEmpty } from '@/utils/types'
import { typeToComponent } from '../../utils'
export default {
name: 'FieldDetailTable',
components: {
draggable,
FieldWrapper,
'FieldInput': () => import('./FieldInput'),
'FieldTextarea': () => import('./FieldTextarea'),
'FieldSelect': () => import('./FieldSelect'),
'FieldCheckbox': () => import('./FieldCheckbox'),
'FieldFile': () => import('./FieldFile'),
'FieldBoolean': () => import('./FieldBoolean'),
'FieldPercent': () => import('./FieldPercent'),
'FieldPosition': () => import('./FieldPosition'),
'FieldLocation': () => import('./FieldLocation'),
'FieldWritingSign': () => import('./FieldWritingSign'),
'FieldDateInterval': () => import('./FieldDateInterval'),
'FieldDescText': () => import('./FieldDescText')
},
filters: {
/** 根据type 找到组件 */
typeToComponentName(item) {
return typeToComponent(item)
}
},
mixins: [mixins],
data() {
return {
dragListConfig: {
delay: 50,
group: {
name: 'childList',
put: ['libList'],
pull: false
},
sort: false,
forceFallback: true,
fallbackClass: 'draggingStyle',
filter: '.empty-box'
},
selectedPoint: [null, null],
tableData: [{}]
}
},
computed: {
isEmpty() {
return isEmpty(this.field.fieldExtendList)
},
isList() {
return true
},
list() {
return this.isEmpty ? [] : this.field.fieldExtendList
}
},
methods: {
dragListEnd(evt) {
// console.log('table drag list end', evt)
},
/**
* 拖拽派发新增事件
* @param evt
*/
dragAdded(evt) {
this.$emit('child-drag-add', this.point, evt)
this.$nextTick(() => {
this.selectedPoint = [evt.newIndex, 0]
})
}
}
}
</script>
<style scoped lang="scss">
.box {
width: 100%;
font-size: 14px;
border-radius: 3px;
border: 1px solid #e1e1e1;
background-color: white;
padding: 5px;
&.is-empty {
border: unset;
background-color: #f7f8fa;
}
.add-btn {
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 10px;
.wk-l-plus {
font-size: 12px;
}
}
.empty-box {
width: 100%;
text-align: center;
background-color: #f7f8fa;
padding: 25px 0;
.empty-box-title {
color: $xr-color-text-primary;
}
.empty-box-desc {
color: $xr-color-text-placeholder;
font-size: 12px;
margin-top: 5px;
}
}
.field-list {
display: flex;
align-items: flex-start;
justify-content: flex-start;
flex-wrap: wrap;
&.is-table {
flex-wrap: nowrap;
overflow-x: auto;
}
}
.input-box {
width: 100%;
height: 30px;
border: 1px solid #dcdfe6;
padding: 3px 0;
}
.table-field {
width: 200px;
/deep/ .field-item {
padding-bottom: 0;
.field-item_title {
border: 1px solid #dcdfe6;
padding-left: 10px;
}
.field-item_body {
padding: 10px 5px;
}
}
}
}
</style>

@ -0,0 +1,41 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
class="field-file"
@click="emitClick"
@action="handleAction">
<div class="box">
请选择文件
</div>
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import mixins from './mixins'
export default {
name: 'FieldFile',
components: {
FieldWrapper
},
mixins: [mixins],
methods: {}
}
</script>
<style scoped lang="scss">
.box {
width: 100px;
text-align: center;
font-size: 14px;
border: 1px solid #e1e1e1;
border-radius: 3px;
background: white;
padding: 10px 0;
}
</style>

@ -0,0 +1,47 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
class="field-input"
@click="emitClick"
@action="handleAction">
<flexbox align="center" class="box">
<span class="default-val">
{{ typeof field.default_value == 'string' ? field.default_value : '' }}
</span>
</flexbox>
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import mixins from './mixins'
export default {
name: 'FieldInput',
components: {
FieldWrapper
},
mixins: [mixins],
methods: {}
}
</script>
<style scoped lang="scss">
.box {
width: 100%;
height: 32px;
font-size: 14px;
border: 1px solid #dcdfe6;
border-radius: $xr-border-radius-base;
background: white;
padding: 0 10px;
.default-val {
color: #333;
}
}
</style>

@ -0,0 +1,45 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
class="field-current-position"
@click="emitClick"
@action="handleAction">
<flexbox align="center" class="box">
<flexbox-item class="default-val" />
<span class="wk wk-icon-location" />
</flexbox>
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import mixins from './mixins'
export default {
name: 'FieldCurrentPosition',
components: {
FieldWrapper
},
mixins: [mixins],
methods: {}
}
</script>
<style scoped lang="scss">
.box {
width: 100%;
height: 32px;
font-size: 14px;
border: 1px solid #dcdfe6;
border-radius: $xr-border-radius-base;
background: white;
padding: 0 10px;
.wk-icon-location {
color: #999999;
}
}
</style>

@ -0,0 +1,52 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
class="field-percent"
@click="emitClick"
@action="handleAction">
<div class="box">
<span class="default-val">{{ field.default_value || '' }}</span>
<span class="rate">%</span>
</div>
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import mixins from './mixins'
export default {
name: 'FieldPercent',
components: {
FieldWrapper
},
mixins: [mixins],
methods: {}
}
</script>
<style scoped lang="scss">
.box {
position: relative;
width: 100%;
height: 32px;
line-height: 32px;
font-size: 14px;
color: #333;
border: 1px solid #dcdfe6;
border-radius: $xr-border-radius-base;
background: white;
padding: 0 30px 0 10px;
.rate {
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-50%);
}
}
</style>

@ -0,0 +1,79 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
class="field-map-position"
@click="emitClick"
@action="handleAction">
<flexbox class="box-select">
<div :class="{placeholder: !Boolean(areaText)}">
{{ areaText || '请选择' }}
</div>
<i class="el-icon-arrow-down el-icon--right"/>
</flexbox>
<div
v-if="field.precisions === 1"
class="box-textarea">
<div :class="{placeholder: !Boolean(detailAddress)}">
{{ detailAddress || '详细地址' }}
</div>
</div>
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import mixins from './mixins'
import { isEmpty } from '@/utils/types'
export default {
name: 'FieldPosition',
components: {
FieldWrapper
},
mixins: [mixins],
computed: {
areaText() {
if (isEmpty(this.field.default_value)) return ''
return this.field.default_value
.filter(o => o.id !== 4)
.map(o => o.name)
.join('/')
},
detailAddress() {
if (isEmpty(this.field.default_value)) return ''
const findRes = this.field.default_value.find(o => o.id === 4)
if (!findRes) return ''
return findRes.name
}
},
methods: {}
}
</script>
<style scoped lang="scss">
.placeholder {
color: #999;
}
.box-select {
width: 100%;
color: #333;
border: 1px solid #dcdfe6;
border-radius: $xr-border-radius-base;
padding: 10px;
div {
flex: 1;
}
}
.box-textarea {
width: 100%;
height: 100px;
border: 1px solid #dcdfe6;
border-radius: $xr-border-radius-base;
margin-top: 10px;
padding: 10px;
}
</style>

@ -0,0 +1,79 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
class="field-select"
@click="emitClick"
@action="handleAction">
<el-radio-group
v-if="field.precisions === 1"
v-model="field.default_value">
<el-radio
v-for="(item, index) in field.setting"
:key="index"
:label="item">
{{ item }}
</el-radio>
</el-radio-group>
<flexbox
v-else
class="select-box">
<div :class="{placeholder: !Boolean(field.default_value)}">
{{ field.default_value ? field.default_value :'请选择' }}
</div>
<i class="el-icon-arrow-down el-icon--right"/>
</flexbox>
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import mixins from './mixins'
export default {
name: 'FieldSelect',
components: {
FieldWrapper
},
mixins: [mixins],
watch: {
field: {
handler() {
//
if (!this.field.precisions) {
this.$set(this.field, 'precisions', 2)
}
},
deep: true,
immediate: true
}
}
}
</script>
<style scoped lang="scss">
.select-box {
width: 100%;
color: #333;
border: 1px solid #dcdfe6;
border-radius: $xr-border-radius-base;
padding: 8px 10px;
div {
flex: 1;
}
.placeholder {
color: #999;
}
}
.el-radio-group {
width: 100%;
.el-radio {
margin: 5px 30px 5px 0;
}
}
</style>

@ -0,0 +1,63 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
class="field-textarea"
@click="emitClick"
@action="handleAction">
<div class="box">
<div class="default-val">
{{ typeof field.default_value == 'string' ? field.default_value : '' }}
</div>
<div class="max-tips">
{{ field.default_value.length+'/'+(field.max_length || 800) }}
</div>
</div>
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import mixins from './mixins'
export default {
name: 'FieldTextarea',
components: {
FieldWrapper
},
mixins: [mixins],
methods: {}
}
</script>
<style scoped lang="scss">
.box {
position: relative;
width: 100%;
height: 80px;
font-size: 14px;
color: #333;
background: white;
border: 1px solid #dcdfe6;
border-radius: $xr-border-radius-base;
padding: 10px 10px 15px 10px;
.default-val {
width: 100%;
height: 48px;
word-break: break-all;
overflow: hidden;
}
.max-tips {
width: 100%;
text-align: right;
font-size: 12px;
color: #999;
word-break: break-all;
overflow: hidden;
}
}
</style>

@ -0,0 +1,216 @@
<template>
<div
:style="{width: fieldWidth}"
:class="{activate: activate}"
class="field-item"
@click.stop="emitClick">
<div v-if="!hiddenTitle" class="field-item_title">
<span class="required">{{ field.is_null ? '*' : '' }}</span>
<span>{{ field.name }}</span>
<span
v-if="field.input_tips"
class="input-tips">
{{ field.input_tips }}
</span>
</div>
<div class="field-item_body">
<slot />
</div>
<template v-if="activate">
<div
v-if="controlFlag.top"
class="control-top control-btn"
@click.stop="handleControl('top', $event)">
<i class="wk wk-icon-top" />
</div>
<div
v-if="controlFlag.bottom"
class="control-bottom control-btn"
@click.stop="handleControl('bottom', $event)">
<i class="wk wk-icon-top bottom" />
</div>
<div
v-if="controlFlag.left"
class="control-left control-btn"
@click.stop="handleControl('left', $event)">
<i class="wk wk-transfer" />
</div>
<div
v-if="controlFlag.right"
class="control-right control-btn"
@click.stop="handleControl('right', $event)">
<i class="wk wk-transfer" />
</div>
<div class="edit-box">
<div
v-if="controlFlag.copy"
class="control-copy control-btn"
@click.stop="handleControl('copy', $event)">
<i class="wk wk-associated" />
</div>
<div
v-if="controlFlag.delete"
class="control-delete control-btn"
@click.stop="handleControl('delete', $event)">
<i class="wk wk-s-delete" />
</div>
</div>
</template>
</div>
</template>
<script>
export default {
name: 'FieldWrapper',
props: {
field: { //
type: Object,
required: true
},
activate: { //
type: Boolean,
default: false
},
controlFlag: { //
type: Object,
default: () => {
return {
top: false,
bottom: false,
left: false,
right: false,
delete: false,
copy: true
}
}
},
hiddenTitle: {
type: Boolean,
default: false
}
},
computed: {
fieldWidth() {
if (!this.field) return '100%'
return this.field.style_percent + '%'
}
},
watch: {
field: {
handler() {
if (this.field && !this.field.style_percent) {
this.field.style_percent = 100
}
},
deep: true,
immediate: true
}
},
methods: {
emitClick(evt) {
this.$emit('click', evt)
},
handleControl(action, evt) {
this.$emit('action', action, evt)
}
}
}
</script>
<style scoped lang="scss">
.field-item {
position: relative;
//border-left: 2px solid transparent;
padding: 0 10px 20px;
background-color: white;
cursor: move;
&.activate {
//border-left: 2px solid $xr-color-primary;
background-color: #f7f8fa;
}
.field-item_title {
min-height: 34px;
line-height: 1.5;
font-size: 13px;
width: 100%;
padding: 10px 0 8px;
word-wrap: break-word;
word-break: break-all;
.required {
color: #F56C6C;
}
.input-tips {
color: #999;
}
}
.field-item_body {}
.control-btn {
position: absolute;
z-index: 1;
width: 25px;
height: 25px;
border-radius: 50%;
box-shadow: 0 2px 4px 0 rgba(163,163,163,.5);
background-color: white;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
.wk {
color: #555;
font-size: 14px;
}
.wk-icon-top {
font-size: 12px;
font-weight: bold;
&.bottom {
transform: rotate(180deg);
}
}
&.control-top {
top: -14px;
left: 50%;
transform: translateX(-50%);
}
&.control-bottom {
bottom: -14px;
left: 50%;
transform: translateX(-50%);
}
&.control-left {
left: -14px;
top: 50%;
transform: translateY(-50%);
}
&.control-right {
right: -14px;
top: 50%;
transform: translateY(-50%);
}
}
.edit-box {
position: absolute;
bottom: -14px;
right: 5%;
z-index: 1;
.control-btn {
position: unset;
display: inline-flex;
vertical-align: middle;
margin: 0 2px;
}
}
}
</style>

@ -0,0 +1,38 @@
<template>
<field-wrapper
:activate="activate"
:field="field"
:control-flag="controlFlag"
class="field-writing-sign"
@click="emitClick"
@action="handleAction">
<div class="box" />
</field-wrapper>
</template>
<script>
import FieldWrapper from './FieldWrapper'
import mixins from './mixins'
export default {
name: 'FieldWritingSign',
components: {
FieldWrapper
},
mixins: [mixins],
methods: {}
}
</script>
<style scoped lang="scss">
.box {
width: 100%;
height: 150px;
border: 1px solid #e1e1e1;
border-radius: 3px;
background: white;
padding: 10px 0;
}
</style>

@ -0,0 +1,13 @@
export { default as FieldInput } from './FieldInput'
export { default as FieldTextarea } from './FieldTextarea'
export { default as FieldSelect } from './FieldSelect'
export { default as FieldCheckbox } from './FieldCheckbox'
export { default as FieldFile } from './FieldFile'
export { default as FieldBoolean } from './FieldBoolean'
export { default as FieldPercent } from './FieldPercent'
export { default as FieldPosition } from './FieldPosition'
export { default as FieldLocation } from './FieldLocation'
export { default as FieldDetailTable } from './FieldDetailTable'
export { default as FieldWritingSign } from './FieldWritingSign'
export { default as FieldDateInterval } from './FieldDateInterval'
export { default as FieldDescText } from './FieldDescText'

@ -0,0 +1,132 @@
import { getFieldAuth } from '../../utils'
import { isEmpty } from '@/utils/types'
export default {
props: {
field: { // 当前字段信息
type: Object,
required: true
},
fieldArr: { // 全部字段数组,为空时则禁止点击改变位置
type: Array,
default: () => []
},
point: { // 当前字段坐标
type: Array
},
activePoint: { // 选中的字段坐标
type: Array,
default: () => []
}
},
data() {
return {
specialFormType: [
// 'detail_table' // 明细表格
]
}
},
computed: {
// 当前字段是否激活
activate() {
return this.point[0] === this.activePoint[0] &&
this.point[1] === this.activePoint[1]
},
/** 只读 */
disabled() {
return !this.fieldAuth.defaultEdit
},
fieldAuth() {
return getFieldAuth(this.field.operating)
},
// 向上操作按钮
topFlag() {
if (isEmpty(this.fieldArr)) return false
// 第一行、上一行有4个、上一行为特殊字段类型不显示
const row = this.point[0]
if (row === 0) return false
const prevRow = this.fieldArr[row - 1]
if (prevRow.length === 4) return false
if (this.specialFormType.includes(prevRow[0].form_type)) return false
return true
},
// 向下操作按钮
bottomFlag() {
if (isEmpty(this.fieldArr)) return false
// 最后一行、当前行只有一个不显示
const row = this.point[0]
if (row === this.fieldArr.length - 1) return false
// if (this.fieldArr[row].length <= 1) return false
return true
},
// 左侧操作按钮
leftFlag() {
if (isEmpty(this.fieldArr)) return false
// 第一列不显示
const column = this.point[1]
if (column === 0) return false
return true
},
// 右侧操作按钮
rightFlag() {
if (isEmpty(this.fieldArr)) return false
// 最后一列不显示
const column = this.point[1]
const row = this.point[0]
if (column === this.fieldArr[row].length - 1) return false
return true
},
// 复制按钮
copyFlag() {
if (isEmpty(this.fieldArr)) return false
return ![
'customer',
'business',
'contacts',
'contract',
'receivables_plan',
'single_user'
].includes(this.field.form_type)
},
controlFlag() {
return {
top: this.topFlag,
bottom: this.bottomFlag,
left: this.leftFlag,
right: this.rightFlag,
delete: this.fieldAuth.deleteEdit,
copy: this.copyFlag
}
}
},
// watch: {
// field: {
// handler() {
// this.$nextTick(() => {
// this.$forceUpdate()
// })
// },
// deep: true,
// immediate: true
// }
// },
methods: {
/**
* click
* @param evt
*/
emitClick(evt) {
this.$emit('click', evt)
},
/**
* 点击删除
* @param action
* @param evt
*/
handleAction(action, evt) {
this.$emit('action', action, this.point, evt)
}
}
}

@ -0,0 +1,372 @@
<template>
<div class="setting-default">
<el-input
v-if="type === 'text'"
v-model="field.default_value"
:maxlength="field.max_length || 100"
:disabled="disabled"
@blur="inputBlur" />
<el-input
v-else-if="type === 'textarea'"
v-model="field.default_value"
:maxlength="field.max_length || 800"
:disabled="disabled"
@blur="inputBlur" />
<el-date-picker
v-else-if="type === 'datePicker'"
v-model="field.default_value"
:disabled="disabled"
:type="field.form_type === 'date' ? 'date' : 'datetime'"
:value-format="field.form_type === 'date' ? 'yyyy-MM-dd' : 'yyyy-MM-dd HH:mm:ss'"
placeholder="请选择" />
<el-date-picker
v-else-if="type === 'date_interval'"
v-model="field.default_value"
:type="field.precisions === 1 ? 'daterange' : 'datetimerange'"
:value-format="field.precisions === 1 ? 'yyyy-MM-dd' : 'yyyy-MM-dd HH:mm:ss'"
:disabled="disabled"
start-placeholder="开始日期"
end-placeholder="结束日期" />
<el-select
v-else-if="type === 'select'"
v-model="field.default_value"
:clearable="canClearable"
:multiple="field.form_type === 'checkbox'"
:disabled="disabled"
placeholder="请选择">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
<template v-else-if="type === 'number'">
<el-input
v-model="field.default_value"
:disabled="disabled"
@blur="inputBlur">
<div
v-if="field.form_type === 'percent'"
slot="suffix"
class="el-input__icon">%</div>
</el-input>
<div class="input-tips">
<span>*</span>
数字的位数必须少于{{ field.form_type === 'percent' ? 10 : 15 }}
</div>
</template>
<template v-else-if="type === 'position'">
<wk-distpicker
v-model="selectedMapValue"
:hide-area="field.precisions >= 3"
:only-province="field.precisions === 4"
:disabled="disabled"
clearable
@change="handleCascaderChange" />
<el-input
v-if="field.precisions === 1"
v-model="detailAddress"
:rows="3"
:maxlength="100"
:disabled="disabled"
type="textarea"
style="margin-top: 5px"
@change="inputPositionChange" />
</template>
</div>
</template>
<script>
import WkDistpicker from '@/components/NewCom/WkDistpicker'
import { isEmpty, isArray } from '@/utils/types'
import { regexIsCRMMobile, regexIsCRMEmail, objDeepCopy } from '@/utils'
import { getFieldAuth } from '../../utils'
export default {
name: 'SettingDefault',
components: {
WkDistpicker
},
props: {
field: {
type: Object,
required: true
}
},
data() {
return {
selectedMapValue: [],
detailAddress: '',
oldPrecisions: null,
areaData: []
}
},
computed: {
//
disabled() {
return !getFieldAuth(this.field.operating).defaultEdit
},
//
canClearable() {
const form_type = this.field.form_type
if ([
'boolean_value'
].includes(form_type)) return false
return true
},
//
type() {
const form_type = this.field.form_type
if ([
'date',
'datetime'
].includes(form_type)) return 'datePicker'
if ([
'number',
'floatnumber',
'percent'
].includes(form_type)) return 'number'
if ([
'select',
'checkbox',
'boolean_value'
].includes(form_type)) return 'select'
switch (this.field.form_type) {
case 'date_interval':
return 'date_interval'
case 'position':
return 'position'
case 'textarea':
return 'textarea'
default:
return 'text'
}
},
//
options() {
if (this.type !== 'select') return []
const form_type = this.field.form_type
if ([
'select',
'checkbox'
].includes(form_type)) {
return this.field.setting.map(o => {
return { label: o, value: o }
})
}
switch (form_type) {
case 'boolean_value':
return [
{ label: '选中', value: '1' },
{ label: '不选中', value: '0' }
]
default:
return []
}
}
},
watch: {
field: {
handler() {
if (this.field.form_type === 'boolean_value') {
//
this.field.default_value = isEmpty(this.field.default_value) ? '0' : this.field.default_value
return
}
if (
this.type === 'select' &&
isEmpty(this.field.setting) &&
!isEmpty(this.field.options)
) {
this.$set(this.field, 'setting', this.field.options.split(','))
}
if (this.type === 'position') {
this.resetDefaultValue()
//
if (isEmpty(this.field.default_value)) {
this.selectedMapValue = []
if (!isArray(this.field.default_value)) {
this.field.default_value = []
}
this.detailAddress = ''
} else {
this.selectedMapValue = this.field.default_value.filter(o => o.id !== 4)
if (this.field.precisions === 1) {
const findRes = this.field.default_value.find(o => o.id === 4)
if (findRes) {
this.detailAddress = findRes.name
} else {
this.detailAddress = ''
}
}
}
}
},
deep: true,
immediate: true
}
},
methods: {
inputBlur() {
// this.$emit('input', this.value)
if (!this.field.default_value) return
if (this.field.form_type === 'mobile') {
//
if (!regexIsCRMMobile(this.field.default_value)) {
this.$message.error('输入的手机格式有误')
this.field.default_value = ''
}
} else if (this.field.form_type === 'email') {
//
if (!regexIsCRMEmail(this.field.default_value)) {
this.$message.error('输入的邮箱格式有误')
this.field.default_value = ''
}
} else if (this.type === 'number') {
//
const num = Number(this.field.default_value) // 0
if (isNaN(num)) {
this.field.default_value = null
return
}
this.field.default_value = String(num)
const arr = String(num).split('.')
const len = String(num)
.replace('.', '')
.replace('-', '')
.length
const maxlength = this.field.form_type === 'percent' ? 10 : 15
if (len > maxlength) {
this.$message.error(`最多支持${maxlength}位数字(包含小数位)`)
this.field.default_value = null
return
}
const min = isEmpty(this.field.minNumRestrict) ? -Infinity : Number(this.field.minNumRestrict || -Infinity)
const max = isEmpty(this.field.maxNumRestrict) ? Infinity : Number(this.field.maxNumRestrict || Infinity)
if (num < min) {
this.$message.error('默认值不能小于最小值')
this.field.default_value = null
return
}
if (num > max) {
this.$message.error('默认值不能大于最大值')
this.field.default_value = null
return
}
// null 0
if (isEmpty(this.field.precisions)) {
this.field.default_value = arr[0]
return
}
if (this.field.precisions === 0) return
if (arr.length > 1 && arr[1].length > Number(this.field.precisions)) {
this.$message.error(`默认值的小数位不能大于${this.field.precisions}`)
this.field.default_value = null
}
}
},
/**
* 修改精度重置默认值
*/
resetDefaultValue() {
if (
!this.oldPrecisions ||
(this.oldPrecisions === this.field.precisions)
) {
this.oldPrecisions = this.field.precisions
return
}
this.oldPrecisions = this.field.precisions
this.selectedMapValue = []
if (!isEmpty(this.field.default_value)) {
this.field.default_value = []
}
},
/**
* 修改详细地址
*/
inputPositionChange() {
if (this.field.precisions !== 1) {
const findIndex = this.field.default_value.findIndex(o => o.id === 4)
if (findIndex === -1) return
this.field.default_value.splice(findIndex, 1)
} else {
const findRes = this.field.default_value.find(o => o.id === 4)
if (findRes) {
findRes.name = this.detailAddress
} else {
this.field.default_value.push({
code: '',
name: this.detailAddress,
id: 4
})
}
}
},
/**
* 选择省市区
*/
handleCascaderChange() {
this.field.default_value = this.selectedMapValue
this.inputPositionChange()
},
getCascaderValArr(data, value) {
const res = []
if (value.length === 0) return res
let index = 0
let _data = objDeepCopy(data)
do {
const findRes = _data.find(o => o.code === value[index])
if (findRes) {
_data = findRes.children || []
res.push({
code: findRes.code,
name: findRes.name,
id: index + 1
})
}
index++
} while (index <= value.length)
return res
}
}
}
</script>
<style scoped lang="scss">
.el-date-editor {
width: 100%;
}
.el-select {
width: 100%;
}
.el-cascader {
width: 100%;
}
.el-input__icon {
color: #333333;
}
.input-tips {
font-size: 12px;
margin-top: 10px;
color: #999;
span {
color: red;
}
}
</style>

@ -0,0 +1,88 @@
<template>
<div class="setting-rich-text">
<tinymce
ref="createTinymce"
v-model="field.default_value"
:init="getEditConfig()"
:height="200"
toolbar="bold italic underline strikethrough | fontselect | forecolor backcolor | fontsizeselect | numlist bullist | alignleft aligncenter alignright | image link | removeformat"
class="rich-txt"
@input="debouncedEditorInput" />
</div>
</template>
<script>
import Tinymce from '@/components/Tinymce'
import { debounce } from 'throttle-debounce'
export default {
name: 'SettingRichText',
components: {
Tinymce
},
props: {
field: {
type: Object,
required: true
}
},
data() {
return {
debouncedEditorInput: null
}
},
created() {
this.debouncedEditorInput = debounce(300, this.editInputChange)
},
methods: {
getEditConfig() {
return {
menubar: false,
statusbar: false,
paste_data_images: true, //
paste_enable_default_filters: false,
placeholder: '描述文字内容',
content_style: ' * {color: #262626; margin: 0;} body { margin: 8px; font-size: 14px; }',
paste_retain_style_properties: 'border', //
toolbar_mode: 'scrolling',
paste_preprocess: function(plugin, args) {
//
var delTag = ['b', 'strong', 'i', 'em']
delTag.forEach(tag => {
var reg = new RegExp(`(<${tag}>)|(</${tag}>)]`, 'g')
args.content = args.content.replace(reg, '')
})
//
var replaceTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
replaceTag.forEach(tag => {
var reg1 = new RegExp(`<${tag}>`, 'g')
var reg2 = new RegExp(`</${tag}>`, 'g')
args.content = args.content.replace(reg1, '<p>')
args.content = args.content.replace(reg2, '</p>')
})
// font
args.content = args.content.replace(/<\/font>/ig, '').replace(/<font[^>]+>/ig, '')
console.log(args.content)
},
paste_postprocess: function(plugin, args) {
var doms = Array.from(args.node.querySelectorAll('*'))
//
doms.forEach(dom => {
dom.style.color = ''
dom.style.fontWeight = ''
dom.style.fontFamily = ''
dom.style.fontSize = ''
dom.style.background = ''
console.log(dom)
})
}
}
},
editInputChange() {}
}
}
</script>
<style scoped>
</style>

@ -0,0 +1,164 @@
<template>
<div class="setting-detail-table">
<div class="item-section">
<div class="name">表格字段</div>
<draggable
:list="field.fieldExtendList"
:options="dragConfig"
@sort="handleChange">
<flexbox
v-for="(item, index) in field.fieldExtendList"
:key="index"
align="center"
justify="flex-start"
class="option-item">
<i
:class="typeObj(item.form_type).icon"
class="type-icon" />
<div class="option-item__name">{{ item.name }}</div>
<el-button
type="text"
class="option-item__icon wk wk-write"
@click="handleEdit(index)" />
<el-button
type="text"
class="option-item__icon wk wk-icon-bin"
@click="handleDelete(index)" />
<div class="option-item__icon drag-hook wk wk-grid" />
</flexbox>
</draggable>
</div>
<div class="item-section">
<div class="name">动作名</div>
<div>
<el-input v-model="field.remark" :maxlength="10" />
</div>
</div>
<div class="item-section">
<div class="name">
填写方式
<el-tooltip
content="选择明细的填写方式"
effect="dark"
popper-class="setting-number-tooltip"
placement="top">
<i class="wk wk-help wk-help-tips" />
</el-tooltip>
</div>
<el-radio-group v-model="field.precisions">
<el-radio :label="1">列表</el-radio>
<el-radio :label="2">表格</el-radio>
</el-radio-group>
</div>
</div>
</template>
<script>
import draggable from 'vuedraggable'
import { guid } from '@/utils'
import FieldTypeLib from '../../fieldTypeLib'
export default {
name: 'SettingDetailTable',
components: {
draggable
},
props: {
field: {
type: Object,
required: true
}
},
data() {
return {
dragConfig: {
group: guid(),
forceFallback: false,
fallbackClass: 'draggingStyle',
handle: '.drag-hook',
filter: '.el-input__inner',
preventOnFilter: false
}
}
},
watch: {
field: {
handler() {
if (!this.field.precisions) {
this.$set(this.field, 'precisions', 1)
}
this.$set(this.field, 'precisions', this.field.precisions)
this.$set(this.field, 'remark', this.field.remark)
},
deep: true,
immediate: true
}
},
methods: {
typeObj(form_type) {
return FieldTypeLib.find(o => o.form_type === form_type)
},
handleChange() {
this.$set(this.field, 'fieldExtendList', this.field.fieldExtendList)
},
handleEdit(index) {
this.$emit('child-edit', this.field.fieldExtendList[index])
},
handleDelete(index) {
this.field.fieldExtendList.splice(index, 1)
this.$set(this.field, 'fieldExtendList', this.field.fieldExtendList)
}
}
}
</script>
<style scoped lang="scss">
.setting-detail-table {
.item-section {
padding: 10px 0;
border-bottom: 1px solid #e6e6e6;
.name {
font-size: 13px;
font-weight: 500;
color: #333;
margin: 10px 0;
}
}
.option-item {
width: 100%;
height: 34px;
border: 1px solid #e6e6e6;
border-radius: 3px;
padding: 0 8px;
margin: 5px 0;
.drag-hook {
cursor: move;
}
.type-icon {
font-size: 14px;
color: #777777;
margin-right: 5px;
}
.option-item__name {
width: 174px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.option-item__icon {
font-size: 14px;
color: #999999;
margin: 0 2px;
}
}
}
</style>

@ -0,0 +1,321 @@
<template>
<div class="setting-logic-form">
<el-button
:disabled="!optionsEditAuth"
class="add-btn"
@click="handleToSet">
点击配置
</el-button>
<el-dialog
:visible.sync="dialogVisible"
:before-close="handleCloseDialog"
:close-on-click-modal="false"
title="添加逻辑表单规则"
width="500px"
class="edit-dialog">
<div>
<div class="edit-tips">
选择选项后才会显示所设置的其他字段
</div>
<div class="edit-table">
<flexbox
align="center"
justify="flex-start"
class="edit-table__header row">
<div class="label">选项内容</div>
<flexbox-item class="content">显示字段</flexbox-item>
</flexbox>
<div
v-if="list.length > 0 && fieldLibArr.length > 0"
class="edit-table__body">
<flexbox
v-for="(item, index) in list"
:key="index"
align="center"
justify="flex-start"
class="row">
<div class="label">{{ item.name }}</div>
<flexbox-item class="content">
<el-select
v-model="item.value"
placeholder="请选择"
multiple>
<el-option
v-for="(item, childIndex) in fieldLibArr"
:key="childIndex"
:label="item.form_type === 'desc_text' ? '描述文字' : (item.name || '未命名')"
:value="item.formAssistId" />
</el-select>
</flexbox-item>
</flexbox>
</div>
</div>
</div>
<div slot="footer">
<el-button @click="handleCloseDialog">
</el-button>
<el-button
type="primary"
@click="handleDialogConfirm">
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
// import { objDeepCopy } from '@/utils/index'
import { isEmpty } from '@/utils/types'
import { getFieldAuth } from '../../utils'
export default {
name: 'SettingLogicForm',
props: {
field: {
type: Object,
required: true
},
fieldArr: { //
type: Array,
required: true
},
point: { //
type: Array,
required: true
}
},
data() {
return {
list: [],
dialogVisible: false
}
},
computed: {
//
optionsEditAuth() {
return getFieldAuth(this.field.operating).optionsEdit
},
fieldLibArr() {
const arr = []
const ids = [] // formAssistId
this.fieldArr.forEach(father => {
father.forEach(child => {
if (child.hasOwnProperty('formAssistId') && !isEmpty(child.formAssistId)) {
ids.push(child.formAssistId)
}
})
})
this.fieldArr.forEach((father, fatherIndex) => {
father.forEach((child, childIndex) => {
if (!child.hasOwnProperty('formAssistId') || isEmpty(child.formAssistId)) {
// formAssistIdformAssistId
child.formAssistId = this.generateFormAssistId(ids)
ids.push(child.formAssistId)
}
if (
fatherIndex !== this.point[0] ||
childIndex !== this.point[1]) {
arr.push(child)
}
})
})
return arr.filter(item => item.form_type !== 'customer' &&
item.form_type !== 'business' &&
item.form_type !== 'contract')
},
allFormAssistId() {
return this.fieldLibArr.map(o => o.formAssistId)
}
},
watch: {
field: {
handler() {
// remark null options_type
if (this.field.remark !== 'options_type') {
//
this.list = this.field.setting.map(o => {
return {
name: o,
value: null
}
})
} else {
//
let data = {}
if (this.field.optionsData) {
data = this.field.optionsData || {}
} else {
try {
data = JSON.parse(this.field.options) || {}
} catch (e) {
this.list = this.field.setting.map(o => {
return {
name: o,
value: null
}
})
//
this.$set(this.field, 'remark', null)
this.$set(this.field, 'optionsData', null)
this.$set(this.field, 'options', this.field.setting.join(','))
return
}
}
this.list = Object.keys(data).map(key => {
return {
name: key,
value: isEmpty(data[key]) ? [] : data[key]
}
})
}
},
deep: true,
immediate: true
},
allFormAssistId: {
handler() {
if (this.field.remark === 'options_type') {
//
this.list.forEach(item => {
const ids = []
if (item.value) {
item.value.forEach(id => {
if (this.allFormAssistId.includes(id)) {
ids.push(id)
}
})
}
item.value = ids
})
const optionsData = {}
this.list.forEach(o => {
optionsData[o.name] = o.value
})
this.$set(this.field, 'optionsData', optionsData)
}
},
deep: true,
immediate: true
}
},
methods: {
/**
* 生成逻辑表单辅助id
*/
generateFormAssistId(ids) {
const startNum = 1000
const generateFn = function(num) {
const id = num + 1
if (ids.includes(id)) {
return generateFn(id)
} else {
return id
}
}
return generateFn(startNum)
},
handleToSet() {
this.dialogVisible = true
},
handleCloseDialog() {
// watch
this.$set(this.field, '_remark', '')
this.$nextTick(() => {
delete this.field._remark
})
this.dialogVisible = false
},
handleDialogConfirm() {
const optionsData = {}
this.list.forEach(o => {
optionsData[o.name] = o.value
})
const len = this.list.filter(o => !isEmpty(o.value)).length
if (len !== 0) {
//
this.$set(this.field, 'remark', 'options_type')
this.$set(this.field, 'optionsData', optionsData)
const optionsStr = JSON.stringify(optionsData)
this.$set(this.field, 'options', optionsStr)
} else {
//
this.$set(this.field, 'remark', null)
this.$set(this.field, 'optionsData', null)
this.$set(this.field, 'options', this.field.setting.join(','))
}
this.handleCloseDialog()
}
}
}
</script>
<style scoped lang="scss">
.add-btn {
width: 100%;
height: 34px;
font-size: 14px;
color: #666666;
border: 1px dashed $xr-border-color-base;
border-radius: $xr-border-radius-base;
background-color: #f8f8f8;
cursor: pointer;
}
.edit-dialog {
.edit-tips {
font-size: 14px;
color: #999999;
background-color: #fafbfb;
border-top: 1px solid #E6E6E6;
border-bottom: 1px solid #E6E6E6;
padding: 5px 20px;
}
/deep/ .el-dialog__body {
padding: 0;
}
.edit-table {
height: 300px;
padding: 0 15px;
overflow-y: auto;
.row {
border-bottom: 1px solid #E6E6E6;
&:last-child {
border-bottom: 0 none;
}
.label {
width: 180px;
padding: 0 15px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.content {
padding-right: 15px;
.el-select {
width: 100%;
}
}
}
.edit-table__header {
width: 100%;
height: 36px;
color: #999999;
}
.edit-table__body {
.row {
padding: 6px 0;
}
}
}
}
</style>

@ -0,0 +1,211 @@
<template>
<div class="setting-number">
<flexbox
align="center"
justify="flex-start"
class="setting-number-item">
<el-checkbox v-model="checked" @change="checkedChange" />
<span style="font-size: 13px;">支持小数</span>
<el-tooltip
content="不选择只能输入整数,勾选后可规定小数位数"
effect="dark"
popper-class="setting-number-tooltip"
placement="top">
<i class="wk wk-help wk-help-tips" />
</el-tooltip>
<template v-if="checked">
<span>限制&nbsp;</span>
<el-select
v-model="field.precisions"
size="small"
placeholder=""
@change="handleSelectChange">
<el-option
v-for="item in precisionList"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
<span>&nbsp;</span>
</template>
</flexbox>
<flexbox
align="flex-start"
justify="flex-start"
direction="column"
class="setting-number-item">
<el-checkbox v-model="limitChecked" @change="limitChange">
限制数值范围
</el-checkbox>
<flexbox
v-if="limitChecked"
align="center"
justify="flex-start"
class="number-range-body">
<flexbox-item>
<el-input-number
v-model="minNumRestrict"
:controls="false"
placeholder="最小值"
@change="handleChangeNumber('minNumRestrict')" />
</flexbox-item>
<div class="number-range-text">~</div>
<flexbox-item>
<el-input-number
v-model="maxNumRestrict"
:controls="false"
placeholder="最大值"
@change="handleChangeNumber('maxNumRestrict')" />
</flexbox-item>
</flexbox>
</flexbox>
</div>
</template>
<script>
import { isEmpty } from '@/utils/types'
export default {
name: 'SettingNumber',
props: {
field: {
type: Object,
required: true
}
},
data() {
return {
checked: false,
precisionList: [],
limitChecked: false,
minNumRestrict: undefined, // undefined input number0
maxNumRestrict: undefined
}
},
watch: {
field: {
handler() {
if ([
'number',
'floatnumber',
'percent'
].includes(this.field.form_type)) {
if (!this.field.hasOwnProperty('minNumRestrict')) {
this.field.minNumRestrict = null
}
if (!this.field.hasOwnProperty('maxNumRestrict')) {
this.field.maxNumRestrict = null
}
this.minNumRestrict = isEmpty(this.field.minNumRestrict) ? undefined : Number(this.field.minNumRestrict)
this.maxNumRestrict = isEmpty(this.field.maxNumRestrict) ? undefined : Number(this.field.maxNumRestrict)
//
const max = this.field.form_type === 'percent' ? 5 : 14
this.precisionList = Array.from({ length: max })
.map((o, i) => {
return { label: i + 1, value: i + 1 }
})
// if (this.field.form_type !== 'percent') {
// this.precisionList.unshift({ label: '', value: 0 })
// }
if (!this.field.hasOwnProperty('precisions')) {
this.field.precisions = this.field.form_type === 'number' ? 4 : 2
}
if (this.field.precisions > max) {
//
this.field.precisions = max
}
this.checked = !isEmpty(this.field.precisions)
this.limitChecked = !isEmpty(this.minNumRestrict) || !isEmpty(this.maxNumRestrict)
}
},
deep: true,
immediate: true
}
},
methods: {
checkedChange() {
if (!this.checked) {
this.field.precisions = null
} else {
this.field.precisions = 2
}
},
handleSelectChange() {
this.$set(this.field, 'precisions', this.field.precisions)
this.$forceUpdate()
},
limitChange() {
if (!this.limitChecked) {
this.minNumRestrict = undefined
this.maxNumRestrict = undefined
this.field.minNumRestrict = ''
this.field.maxNumRestrict = ''
}
},
handleChangeNumber(type) {
const current = this[type]
const len = String(current || '')
.replace('.', '')
.replace('-', '')
.length
const maxlength = this.field.form_type === 'percent' ? 10 : 15
if (len > maxlength) {
this.$message.error(`最多支持${maxlength}位数字`)
this.field[type] = null
return
}
const min = this.minNumRestrict
const max = this.maxNumRestrict
if (!isEmpty(min) && !isEmpty(max)) {
if (Number(min) > Number(max)) {
this.$message.error('请输入正确的数值范围')
this.field[type] = null
}
}
const minNum = isEmpty(min) ? '' : min
const maxNum = isEmpty(max) ? '' : max
this.field.minNumRestrict = this.minNumRestrict !== null ? String(minNum) : null
this.field.maxNumRestrict = this.maxNumRestrict !== null ? String(maxNum) : null
}
}
}
</script>
<style>
.setting-number-tooltip {
max-width: 250px;
}
</style>
<style scoped lang="scss">
.setting-number-item {
height: 32px;
&:nth-child(2) {
height: auto;
margin-top: 10px;
}
.el-checkbox {
margin-right: 8px;
}
.el-tooltip {
margin: 0 10px 0 5px;
}
.el-select {
width: 70px;
}
.el-input-number {
width: 100%;
}
.number-range-body {
margin-top: 10px;
}
.number-range-text {
padding: 0 10px;
}
}
</style>

@ -0,0 +1,395 @@
<template>
<div class="setting-options">
<draggable
:list="optionsList"
:options="dragConfig"
@sort="handleChange">
<div
v-for="(item, index) in optionsList"
:key="index"
class="option-item">
<el-input
:disabled="!optionsEditAuth"
v-model="item.value"
@change="handleChange">
<flexbox v-if="optionsEditAuth" slot="suffix">
<div class="el-input__icon drag-hook wk wk-grid" />
<el-button
type="text"
class="el-input__icon wk wk-icon-bin"
@click="handleDelete(index)" />
</flexbox>
</el-input>
</div>
</draggable>
<div
v-if="showOther"
class="option-item other-item">
<el-input
value="其他"
disabled>
<flexbox v-if="optionsEditAuth" slot="suffix">
<el-button
type="text"
class="el-input__icon wk wk-icon-bin"
@click="handleDelete(-1)" />
</flexbox>
</el-input>
</div>
<el-button
v-if="optionsEditAuth"
class="add-btn"
@click="handleAdd">
<i class="el-icon-plus" /> 添加新选项
</el-button>
<flexbox
v-if="optionsEditAuth"
align="center"
justify="center">
<div class="add-other-btn" @click="handleAddOther">
添加其他
</div>
<flexbox-item />
<div class="add-other-btn" @click="handleUpdateAll"></div>
</flexbox>
<el-dialog
:visible.sync="dialogVisible"
:before-close="handleCloseDialog"
title="批量编辑"
width="500px"
class="edit-dialog">
<div>
<div class="edit-tips">
每行内容对应一个选项点击完成后逻辑表单设置将失效
</div>
<el-input
v-model="dialogContentVal"
:rows="10"
resize="none"
type="textarea" />
</div>
<div slot="footer">
<el-button @click="handleCloseDialog">
</el-button>
<el-button
type="primary"
@click="handleDialogConfirm">
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import draggable from 'vuedraggable'
import { isEmpty, isArray } from '@/utils/types'
import { getFieldAuth } from '../../utils'
import { guid } from '@/utils'
export default {
name: 'SettingOptions',
components: {
draggable
},
props: {
field: {
type: Object,
required: true
},
isTableChild: {
type: Boolean,
default: false
}
},
data() {
return {
optionsList: [],
dialogVisible: false,
dialogContentVal: ''
}
},
computed: {
optionsEditAuth() {
return getFieldAuth(this.field.operating).optionsEdit
},
showOther() {
return this.field.setting.includes('其他')
},
dragConfig() {
return {
group: guid(),
forceFallback: false,
disabled: !this.optionsEditAuth,
fallbackClass: 'draggingStyle',
handle: '.drag-hook',
filter: '.el-input__inner',
preventOnFilter: false
}
}
},
watch: {
field: {
handler(newVal, oldVal) {
if (isEmpty(this.field.options)) {
this.$set(this.field, 'options', '选1,选2,选3')
this.$set(this.field, 'setting', ['选1', '选2', '选3'])
}
if (!oldVal || newVal.options !== oldVal.options) {
this.optionsList = this.field.setting
.filter(o => o !== '其他')
.map(o => {
return { value: o }
})
}
},
deep: true,
immediate: true
}
},
methods: {
/**
* 修改排序
*/
handleChange() {
//
let arr = this.optionsList
.map(o => o.value)
.filter(o => !isEmpty(o) && o !== '其他')
arr = Array.from(new Set(arr))
if (arr.length !== this.optionsList.length) {
this.optionsList = arr.map(o => {
return { value: o }
})
}
if (this.showOther) {
arr.push('其他')
}
this.field.setting = arr
if (this.field.remark === 'options_type') {
//
const optionsData = {}
const keys = Object.keys(this.field.optionsData)
this.optionsList.forEach(o => {
if (keys.includes(o.value)) {
optionsData[o.value] = this.field.optionsData[o.value]
} else {
optionsData[o.value] = []
}
})
Object.keys(optionsData).forEach(key => {
const findRes = this.optionsList.find(o => o.value === key && key !== '其他')
if (!findRes) {
delete optionsData[key]
}
})
if (!this.showOther) {
delete optionsData['其他']
} else if (keys.includes('其他')) {
optionsData['其他'] = this.field.optionsData['其他']
}
this.field.options = JSON.stringify(optionsData)
this.$set(this.field, 'optionsData', optionsData)
} else {
//
this.field.options = arr.join(',')
}
this.$set(this.field, 'setting', this.field.setting)
this.$set(this.field, 'options', this.field.options)
this.$nextTick(() => {
this.checkDefaultValue()
})
},
/**
* 删除
* @param index
*/
handleDelete(index) {
let delItem = null
if (index !== -1) {
delItem = this.field.setting[index]
this.optionsList.splice(index, 1)
this.field.setting.splice(index, 1)
} else {
const findIndex = this.field.setting.lastIndexOf('其他')
if (findIndex !== -1) {
this.field.setting.splice(findIndex, 1)
delItem = '其他'
}
}
if (this.field.remark === 'options_type') {
//
delete this.field.optionsData[delItem]
this.$set(this.field, 'options', JSON.stringify(this.field.optionsData))
} else {
//
this.$set(this.field, 'options', this.field.setting.join(','))
}
this.$set(this.field, 'setting', this.field.setting)
this.checkDefaultValue()
},
/**
* 添加
*/
handleAdd() {
const val = this.getAddValue(this.optionsList.length + 1)
this.optionsList.push({
value: val
})
this.handleChange()
},
/**
* 添加其他
*/
handleAddOther() {
if (this.field.setting.indexOf('其他') === -1) {
this.field.setting.push('其他')
}
this.$set(this.field, 'setting', this.field.setting)
if (this.field.remark === 'options_type') {
//
this.field.optionsData['其他'] = []
this.$set(this.field, 'optionsData', this.field.optionsData)
this.$set(this.field, 'options', JSON.stringify(this.field.optionsData))
} else {
//
this.$set(this.field, 'options', this.field.setting.join(','))
}
},
/**
* 点击批量编辑
*/
handleUpdateAll() {
this.dialogContentVal = this.optionsList.map(o => o.value).join('\n')
this.dialogVisible = true
},
/**
* 关闭弹窗
*/
handleCloseDialog() {
this.dialogVisible = false
},
/**
* 批量编辑
*/
handleDialogConfirm() {
let arr = this.dialogContentVal.split(/\n|\r/)
arr = Array.from(new Set(arr))
.map(o => o.trim())
.filter(o => !isEmpty(o) && o !== '其他')
this.optionsList = arr.map(o => {
return { value: o }
})
this.$set(this.field, 'remark', null)
this.$set(this.field, 'optionsData', null)
this.handleChange()
this.handleCloseDialog()
},
getAddValue(index) {
const findRes = this.optionsList.find(o => o.value === `${index}`)
if (findRes) {
return this.getAddValue(index + 1)
}
return `${index}`
},
/**
* 选项变化后修改默认值
*/
checkDefaultValue() {
if (!isEmpty(this.field.default_value)) {
if (isArray(this.field.default_value)) {
const arr = []
this.field.default_value.forEach(o => {
const findRes = this.optionsList.find(item => item.value === o)
if (findRes) arr.push(o)
})
this.$set(this.field, 'default_value', [...arr])
} else {
const findRes = this.optionsList.find(item => item.value === this.field.default_value)
if (!findRes) {
this.$set(this.field, 'default_value', null)
}
}
}
}
}
}
</script>
<style scoped lang="scss">
.option-item {
margin: 5px 0;
.el-input__icon {
font-size: 14px;
color: #999999;
}
.drag-hook {
cursor: move;
}
&.other-item {
.wk-icon-bin {
cursor: pointer;
}
}
}
.add-btn {
width: 100%;
height: 34px;
font-size: 14px;
color: #666666;
border: 1px dashed $xr-border-color-base;
border-radius: $xr-border-radius-base;
background-color: #f8f8f8;
cursor: pointer;
}
.add-other-btn {
font-size: 14px;
color: #666666;
cursor: pointer;
display: inline-block;
margin-top: 8px;
}
.edit-dialog {
.edit-tips {
font-size: 14px;
//color: #999999;
color: #ecb971;
background-color: #fafbfb;
border-top: 1px solid #E6E6E6;
border-bottom: 1px solid #E6E6E6;
padding: 5px 20px;
}
.el-textarea {
margin: 10px 0;
}
/deep/ .el-textarea__inner {
border: 0 none;
padding: 10px 20px;
}
/deep/ .el-dialog__body {
padding: 0;
}
}
</style>

@ -0,0 +1,83 @@
<template>
<div class="setting-precisions">
<el-select
:disabled="!optionsEditAuth"
v-model="field.precisions"
placeholder="请选择">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</div>
</template>
<script>
import { getFieldAuth } from '../../utils'
export default {
name: 'SettingPrecisions',
props: {
field: {
type: Object,
required: true
}
},
data() {
return {
options: []
}
},
computed: {
//
optionsEditAuth() {
return getFieldAuth(this.field.operating).optionsEdit
}
},
watch: {
field: {
handler() {
if (![
'date_interval',
'position',
'select',
'checkbox'
].includes(this.field.form_type)) return
if (this.field.form_type === 'date_interval') {
this.options = [
{ label: '日期', value: 1 },
{ label: '日期时间', value: 2 }
]
} else if (this.field.form_type === 'position') {
this.options = [
{ label: '省/地区、市、区/县、详细地址', value: 1 },
{ label: '省/地区、市、区/县', value: 2 },
{ label: '省/地区、市', value: 3 },
{ label: '省/地区', value: 4 }
]
} else {
this.options = [
{ label: '平铺', value: 1 },
{ label: '下拉', value: 2 }
]
if (!this.field.precisions) {
this.$set(this.field, 'precisions', this.field.form_type === 'checkbox' ? 1 : 2)
}
}
if (!this.field.precisions) {
this.$set(this.field, 'precisions', 1)
}
},
deep: true,
immediate: true
}
}
}
</script>
<style scoped>
.el-select {
width: 100%;
}
</style>

@ -0,0 +1,325 @@
<template>
<div
v-clickoutside="clickOutSide"
v-if="typeObj"
class="field-setting">
<div class="setting-title">
{{ typeObj.name }}
</div>
<div class="setting-body">
<template v-if="!isDescText">
<div class="item-section">
<div class="name">标识名</div>
<el-input
v-model="field.name"
:disabled="!fieldAuth.nameEdit"/>
<div class="input-tips"><span>*</span>标识名不能为空</div>
</div>
<div class="item-section">
<div class="name">提示文字</div>
<el-input
v-model="field.input_tips"
:rows="3"
type="textarea"
resize="none"/>
<div class="input-tips"><span>*</span>显示在标识名右侧的说明文字</div>
</div>
<setting-detail-table
v-if="field.form_type === 'detail_table'"
:field="field"
@child-edit="emitChildEdit" />
<template v-if="canOptions">
<div class="item-section">
<div class="name">选项内容</div>
<div class="input-tips"><span>*</span>修改选项后该项设置的逻辑表单会失效</div>
<setting-options
:field="field"
:is-table-child="isTableChild" />
</div>
<div v-if="!isTableChild" class="item-section">
<div class="name">逻辑表单</div>
<setting-logic-form
:field="field"
:point="point"
:field-arr="fieldArr" />
</div>
</template>
<div v-if="canPrecisions" class="item-section">
<div class="name">
{{ precisionsTitle }}
</div>
<setting-precisions :field="field" />
</div>
<div v-if="canDefault" class="item-section">
<div class="name">默认值</div>
<setting-default :field="field" />
</div>
<div v-if="canNumber" class="item-section">
<setting-number :field="field" />
</div>
</template>
<div v-if="isDescText" class="item-section">
<div class="name">内容</div>
<setting-desc-text :field="field" />
</div>
<div v-if="fieldAuth.percentEdit" class="item-section">
<div class="name">
字段占比 %
<el-tooltip
content="配置表单布局,可以单行多字段排布"
effect="dark"
placement="top">
<i class="wk wk-help wk-help-tips" style="margin-left: 3px;"/>
</el-tooltip>
</div>
<el-radio-group
v-model="field.style_percent"
size="medium"
@change="emitUpdateWidth">
<el-radio-button
v-for="item in widthOptions"
:label="item.value"
:key="item.value">{{ item.value }}</el-radio-button>
</el-radio-group>
</div>
<div v-if="canTransform && transformData && transformData[field.form_type]" class="item-section">
<div class="name">转化客户字段</div>
<el-select
v-model="field.relevant"
clearable>
<el-option
v-for="item in transformData[field.form_type]"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
</div>
<template v-if="!isDescText">
<div
v-if="fieldAuth.nullEdit"
class="item-check-section">
<el-checkbox
v-model="field.is_null"
:true-label="1"
:false-label="0">设为必填</el-checkbox>
</div>
<div
v-if="fieldAuth.uniqueEdit"
class="item-check-section">
<el-checkbox
v-model="field.is_unique"
:true-label="1"
:false-label="0">设为唯一</el-checkbox>
</div>
<div
v-if="fieldAuth.hiddenEdit"
class="item-check-section">
<el-checkbox
v-model="field.is_hidden"
:true-label="1"
:false-label="0">隐藏字段</el-checkbox>
</div>
</template>
</div>
</div>
</template>
<script>
import SettingDefault from './SettingDefault'
import SettingOptions from './SettingOptions'
import SettingNumber from './SettingNumber'
import SettingPrecisions from './SettingPrecisions'
import SettingDescText from './SettingDescText'
import SettingDetailTable from './SettingDetailTable'
import SettingLogicForm from './SettingLogicForm'
import FieldTypeLib from '../../fieldTypeLib'
import { getFieldAuth } from '../../utils'
export default {
name: 'FieldSetting',
components: {
SettingDefault,
SettingOptions,
SettingNumber,
SettingPrecisions,
SettingDescText,
SettingDetailTable,
SettingLogicForm
},
props: {
//
canTransform: Boolean,
transformData: Object,
field: { //
type: Object,
required: true
},
fieldArr: { //
type: Array,
required: true
},
point: { //
type: Array,
required: true
}
},
data() {
return {
widthOptions: [
{ value: 25 },
{ value: 50 },
{ value: 75 },
{ value: 100 }
],
stylePercentValue: []
}
},
computed: {
typeObj() {
const field = FieldTypeLib.find(o => o.form_type === this.field.form_type)
return field || this.field
},
fieldAuth() {
return getFieldAuth(this.field.operating)
},
//
canDefault() {
return ![
'user',
'structure',
'file',
'location',
'handwriting_sign',
'detail_table'
].includes(this.field.form_type)
},
//
canOptions() {
return [
'select',
'checkbox'
].includes(this.field.form_type)
},
//
canNumber() {
return [
'number',
'floatnumber',
'percent'
].includes(this.field.form_type)
},
//
canPrecisions() {
return [
'date_interval',
'position',
'select',
'checkbox'
].includes(this.field.form_type)
},
//
precisionsTitle() {
if (!this.canPrecisions) return ''
switch (this.field.form_type) {
case 'date_interval':
return '日期类型'
case 'position':
return '地址精度'
case 'select':
return '展示方式'
case 'checkbox':
return '展示方式'
default:
return '精度'
}
},
//
isDescText() {
return this.field.form_type === 'desc_text'
},
//
isTableChild() {
const fatherField = this.fieldArr[this.point[0]][this.point[1]]
return fatherField.form_type === 'detail_table'
}
},
watch: {
field: {
handler() {
this.stylePercentValue = [Number(this.field.style_percent) || 100]
},
deep: true,
immediate: true
}
},
methods: {
emitUpdateWidth() {
this.$emit('update-width')
},
emitChildEdit(field = null) {
this.$emit('child-edit', field)
},
clickOutSide() {
this.emitChildEdit()
}
}
}
</script>
<style scoped lang="scss">
.el-checkbox /deep/ .el-checkbox__label {
font-size: 13px;
color: #333333;
}
.field-setting {
.setting-title {
padding: 20px 15px 0;
font-weight: bold;
}
.setting-body {
padding: 0 15px 10px;
.input-tips {
font-size: 12px;
margin-top: 10px;
color: #999;
span {
color: red;
}
}
.item-section {
padding: 10px 0;
border-bottom: 1px solid #e6e6e6;
.name {
font-size: 13px;
font-weight: 500;
color: #333;
margin: 10px 0;
}
}
.item-check-section {
margin-top: 10px;
}
}
}
</style>

@ -0,0 +1,39 @@
export default class Field {
constructor(obj) {
this.field_type = 0 // 新增字段默认加入0 1是系统字段 2 客户行业 级别 来源 等 3 特殊
this.field_id = obj.field_id || '' // 字段id 1
this.name = obj.name || '' // 标识名 1
this.form_type = obj.form_type || '' // 字段类型 1
this.is_unique = obj.is_unique || 0 // 是否唯一
this.is_null = obj.is_null || 0 // 是否必填
this.is_hidden = obj.is_hidden || 0 // 是否隐藏字段
this.input_tips = obj.input_tips || '' // 输入提示
if (this.form_type === 'textarea') {
this.max_length = obj.max_length || 800 // textarea 多行文本有最大数量
}
if (this.form_type === 'checkbox') {
this.default_value = obj.default_value || []
} else {
this.default_value = obj.default_value || ''
}
// 表格的特殊处理
if (this.form_type === 'form') {
this.formValue = obj.formValue || [] // 内部布局
}
this.setting = obj.setting || [] // 单选选项
// this.showSetting = obj.showSetting || [] // 单选选项
// this.componentName = '' // 组件名字
this.is_deleted = 0 // 是删除标示这个字段是无效的 1是无效的
}
// 校验数据
check() {
if (this.name === '') {
return '字段名称不能为空'
}
return ''
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save