diff --git a/api/index.js b/api/index.js index 58d22f3..4482ffa 100644 --- a/api/index.js +++ b/api/index.js @@ -33,10 +33,17 @@ export const setCertificateInformationEditPost = (params) => post("/app/specialU export const getDeptTree = (params) => post("/api/department/listzTree", params) //用户信息 export const getEMPLOYMENTAPPLYMANAGEMENTID = (params) => post("/app/user/getEMPLOYMENTAPPLYMANAGEMENTID", params) //获取EMPLOYMENTAPPLYMANAGEMENTID -export const getCorpInfoList = (params) => post("/department/getPreventCrop", params) //获取公司列表 -export const getConfinedSpaceSelectList = (params) => post("/app/gfLimitSpace/listAllSpace", { + +// 以下接口八项作业流程使用 +export const getCorpInfoList = (params) => post("/app/util/getCorp", params) //获取公司列表 +export const getConfinedSpaceSelectList = (params) => post("/app/Task/getAllToDoList", { loading: false, ...params }) //获取受限空间名称选择列表 -export const getOtherAssignmentsSelectList = (params) => post("/app/eightwork/list", params) //关联的其他特殊作业及安全作业票编号选择列表 -export const getRiskIdentificationResultsSelectList = (params) => post("/app/eightwork/getInfo", params) //风险辨识结果选择列表 +export const getOtherAssignmentsSelectList = (params) => post("/app/Task/getAllUnEndList", params) //关联的其他特殊作业及安全作业票编号选择列表 +export const getRiskIdentificationResultsSelectList = (params) => post("/app/eightWork/getInfo", params) //风险辨识结果选择列表 +export const setTaskSave = (params) => post("/app/Task/init", params) //八项作业、隐患整改、安全环保检查任务保存更新接口 +export const getCurrentNextOperation = (params) => post("/app/Task/getSupplementInfo", params) //获取当前任务的下一步操作 +export const getDepartmentTree = (params) => post("/app/util/getDepartmentTree", params) //获取部门树 +export const getUserList = (params) => post("/app/util/getUserList", params) //获取人员 +// 结束 diff --git a/components/other-select/index.vue b/components/other-select/index.vue index 23e9b27..859f354 100644 --- a/components/other-select/index.vue +++ b/components/other-select/index.vue @@ -5,7 +5,7 @@ <u-checkbox-group v-model="selectValue" placement="column"> <u-checkbox :customStyle="{marginBottom: '8px'}" v-for="(item, index) in list" :key="index" :label="type === 'assignments' ? item.CHECK_NO : item.NAME" - :value="type === 'assignments' ? item.CHECK_NO : item.NAME"> + :name="type === 'assignments' ? item.CHECK_NO : item.NAME"> </u-checkbox> </u-checkbox-group> <view class="button_group_placeholder"></view> @@ -29,17 +29,14 @@ visible: { type: Boolean, required: true, - default: false }, value: { type: String, required: true, - default: '' }, type: { type: String, required: true, - default: '' } }, data() { @@ -51,7 +48,10 @@ watch: { visible: { handler(newValue) { - if (newValue) this.getData() + if (newValue) { + this.selectValue = this.value.split(',') + this.getData() + } }, immediate: true } @@ -60,7 +60,7 @@ async getData() { if (this.type === 'assignments') { const resData = await getOtherAssignmentsSelectList() - this.list = resData.varList + this.list = resData.list } else if (this.type === 'identification') { const resData = await getRiskIdentificationResultsSelectList({ vectors: JSON.stringify(['accidentType']) @@ -69,17 +69,19 @@ } }, close() { + this.selectValue = [] this.$emit('update:visible', false) }, determine() { - const value = [] - for (let i = 0; i < this.selectValue.length; i++) { - if (this.value.indexOf(this.selectValue[i]) === -1) { - value.push(this.selectValue[i]) - } - } - this.value && value.unshift(this.value) - const emitValue = value.join(',') + // const value = [] + // for (let i = 0; i < this.selectValue.length; i++) { + // if (this.value.indexOf(this.selectValue[i]) === -1) { + // value.push(this.selectValue[i]) + // } + // } + // this.value && value.unshift(this.value) + // const emitValue = value.join(',') + const emitValue = this.selectValue.join(',') this.$emit('input', emitValue) this.close() } @@ -107,4 +109,4 @@ right: 0; } } -</style> \ No newline at end of file +</style> diff --git a/pages.json b/pages.json index 8a9df44..a3c5e2e 100644 --- a/pages.json +++ b/pages.json @@ -175,7 +175,7 @@ } }, { - "path": "pages/selete_corp_info/index", + "path": "pages/eight_assignments/select_corp_info", "style": { "navigationBarTitleText": "选择分公司" } @@ -197,6 +197,18 @@ "style": { "navigationBarTitleText": "位置定位" } + }, + { + "path": "pages/eight_assignments/select_review_personnel", + "style": { + "navigationBarTitleText": "选择审核人员" + } + }, + { + "path": "pages/eight_assignments/select_department", + "style": { + "navigationBarTitleText": "选择部门" + } } ], "globalStyle": { diff --git a/pages/eight_assignments/confined_space/apply.vue b/pages/eight_assignments/confined_space/apply.vue index 0d79121..e73a63c 100644 --- a/pages/eight_assignments/confined_space/apply.vue +++ b/pages/eight_assignments/confined_space/apply.vue @@ -1,173 +1,234 @@ <template> - <view class="content"> - <view class="card"> - <u--form labelPosition="left" :model="form" :rules="rules" ref="formRef" labelWidth="140px"> - <u-form-item label="申请单位" prop="APPLY_DEPARTMENT_NAME" borderBottom required> - <u--input v-model="form.APPLY_DEPARTMENT_NAME" border="none" readonly /> - </u-form-item> - <u-form-item label="申请人" prop="APPLY_USER_NAME" borderBottom required> - <u--input v-model="form.APPLY_USER_NAME" border="none" readonly /> - </u-form-item> - <u-form-item label="作业分公司" prop="CORP_NAME" borderBottom required> - <u--input v-model="form.CORP_NAME" border="none" readonly /> - </u-form-item> - <u-form-item label="受限空间所属单位" prop="BELONG_CORP" borderBottom required> - <u--input v-model="form.BELONG_CORP" border="none" /> - </u-form-item> - <u-form-item label="受限空间名称" prop="LIMITSPACE_NAME" borderBottom required - @click="fnSingleChoiceClick('LIMITSPACE_NAME')"> - <u--input v-model="form.LIMITSPACE_NAME" border="none" readonly /> - </u-form-item> - <u-form-item label="受限空间内原有介质名称" prop="MEDIUM_NAME" borderBottom required> - <u--input v-model="form.MEDIUM_NAME" border="none" /> - </u-form-item> - <u-form-item label="作业内容" prop="WORK_CONTENT" borderBottom required labelPosition="top"> - <u--textarea v-model="form.WORK_CONTENT" border="none" autoHeight /> - </u-form-item> - <u-form-item label="作业开始时间" prop="WORK_START_DATE" borderBottom required - @click="fnDateTimePickerClick('WORK_START_DATE')"> - <u--input v-model="form.WORK_START_DATE" border="none" readonly /> - </u-form-item> - <u-form-item label="作业结束时间" prop="WORK_END_DATE" borderBottom required - @click="fnDateTimePickerClick('WORK_END_DATE')"> - <u--input v-model="form.WORK_END_DATE" border="none" readonly /> - </u-form-item> - <u-form-item label="地点坐标" borderBottom required> - <u-button type="primary" size="small" text="定位" :customStyle="{width:'100upx',margin:0}" - @click="fnLocation" /> - </u-form-item> - <u-form-item label="经度" prop="WORK_LONGITUDE" borderBottom required> - <u--input v-model="form.WORK_LONGITUDE" border="none" readonly /> - </u-form-item> - <u-form-item label="纬度" prop="WORK_LATITUDE" borderBottom required> - <u--input v-model="form.WORK_LATITUDE" border="none" readonly /> - </u-form-item> - <u-form-item label="作业人" prop="WORK_USER" borderBottom required> - <u--input v-model="form.WORK_USER" border="none" /> - </u-form-item> - <u-form-item label="关联的其他特殊作业及安全作业票编号" prop="SPECIAL_WORK" borderBottom required labelPosition="top" - labelWidth="auto"> - <u-button type="primary" size="mini" text="选择其它" - :customStyle="{position: 'absolute',top: '-46upx',right: '20upx',width: '150upx'}" - @click="otherAssignmentsShow = true" /> - <u--textarea v-model="form.SPECIAL_WORK" border="none" autoHeight maxlength="-1" /> - </u-form-item> - <u-form-item label="风险辨识结果" prop="RISK_IDENTIFICATION" borderBottom required labelPosition="top" - labelWidth="auto"> - <u-button type="primary" size="mini" text="选择其它" - :customStyle="{position: 'absolute',top: '-46upx',right: '20upx',width: '150upx'}" - @click="otherIdentificationShow = true" /> - <u--textarea v-model="form.RISK_IDENTIFICATION" border="none" autoHeight maxlength="-1" /> - </u-form-item> - </u--form> - </view> - <u-picker :show="picker.show" :columns="picker.columns" :keyName="picker.key" :key="picker.type" - @confirm="fnSingleChoiceConfirm" @cancel="fnSingleChoiceCancel" /> - <u-datetime-picker :show="datetimePicker.show" v-model="datetimePicker.value" :mode="datetimePicker.mode" - :minDate="datetimePicker.min" :key="datetimePicker.type" @confirm="fnDateTimePickerConfirm" - @cancel="fnDateTimePickerCancel" /> - <other-select :visible.sync="otherAssignmentsShow" v-model="form.SPECIAL_WORK" type="assignments" /> - <other-select :visible.sync="otherIdentificationShow" v-model="form.RISK_IDENTIFICATION" type="identification" /> - </view> + <view class="content"> + <view class="card"> + <u-form labelPosition="left" :model="form" :rules="rules" ref="formRef" labelWidth="140px"> + <u-form-item label="申请单位" prop="APPLY_DEPARTMENT_NAME" borderBottom required> + <u-input v-model="form.APPLY_DEPARTMENT_NAME" border="none" readonly/> + </u-form-item> + <u-form-item label="申请人" prop="APPLY_USER_NAME" borderBottom required> + <u-input v-model="form.APPLY_USER_NAME" border="none" readonly/> + </u-form-item> + <u-form-item label="作业分公司" prop="CORP_NAME" borderBottom required> + <u-input v-model="form.CORP_NAME" border="none" readonly/> + </u-form-item> + <u-form-item label="受限空间所属单位" prop="BELONG_CORP" borderBottom required> + <u-input v-model="form.BELONG_CORP" border="none"/> + </u-form-item> + <u-form-item label="受限空间名称" prop="LIMITSPACE_NAME" borderBottom required + @click="fnSingleChoiceClick('LIMITSPACE_NAME')"> + <u-input v-model="form.LIMITSPACE_NAME" border="none" readonly/> + <u-icon name="arrow-right"></u-icon> + </u-form-item> + <u-form-item label="受限空间内原有介质名称" prop="MEDIUM_NAME" borderBottom required> + <u-input v-model="form.MEDIUM_NAME" border="none"/> + </u-form-item> + <u-form-item label="作业内容" prop="WORK_CONTENT" borderBottom required labelPosition="top"> + <u-textarea v-model="form.WORK_CONTENT" border="none" autoHeight/> + </u-form-item> + <u-form-item label="作业开始时间" prop="WORK_START_DATE" borderBottom required + @click="fnDateTimePickerClick('WORK_START_DATE')"> + <u-input v-model="form.WORK_START_DATE" border="none" readonly/> + <u-icon name="arrow-right"></u-icon> + </u-form-item> + <u-form-item label="作业结束时间" prop="WORK_END_DATE" borderBottom required + @click="fnDateTimePickerClick('WORK_END_DATE')"> + <u-input v-model="form.WORK_END_DATE" border="none" readonly/> + <u-icon name="arrow-right"></u-icon> + </u-form-item> + <u-form-item label="地点坐标" borderBottom required> + <u-button type="primary" size="small" text="定位" :customStyle="{width:'100upx',margin:0}" + @click="fnLocation"/> + </u-form-item> + <u-form-item label="经度" prop="WORK_LONGITUDE" borderBottom required> + <u-input v-model="form.WORK_LONGITUDE" border="none" readonly/> + </u-form-item> + <u-form-item label="纬度" prop="WORK_LATITUDE" borderBottom required> + <u-input v-model="form.WORK_LATITUDE" border="none" readonly/> + </u-form-item> + <u-form-item label="作业人" prop="WORK_USER" borderBottom required> + <u-input v-model="form.WORK_USER" border="none"/> + </u-form-item> + <u-form-item label="关联的其他特殊作业及安全作业票编号" prop="SPECIAL_WORK" borderBottom required + labelPosition="top" labelWidth="auto"> + <u-button type="primary" size="mini" text="选择其它" + :customStyle="{position: 'absolute',top: '-46upx',right: '20upx',width: '150upx'}" + @click="otherAssignmentsShow = true"/> + <u-textarea v-model="form.SPECIAL_WORK" border="none" autoHeight maxlength="-1"/> + </u-form-item> + <u-form-item label="风险辨识结果" prop="RISK_IDENTIFICATION" borderBottom required labelPosition="top" + labelWidth="auto"> + <u-button type="primary" size="mini" text="选择其它" + :customStyle="{position: 'absolute',top: '-46upx',right: '20upx',width: '150upx'}" + @click="otherIdentificationShow = true"/> + <u-textarea v-model="form.RISK_IDENTIFICATION" border="none" autoHeight maxlength="-1"/> + </u-form-item> + </u-form> + <view class="mt-10"> + <u-button type="primary" text="下一步" @click="$u.debounce(fnSubmit, 1000,true)"/> + </view> + </view> + <u-picker :show="picker.show" :columns="picker.columns" :keyName="picker.key" :key="picker.type" + @confirm="fnSingleChoiceConfirm" @cancel="fnSingleChoiceCancel"/> + <u-datetime-picker :show="dateTimePicker.show" v-model="dateTimePicker.value" :mode="dateTimePicker.mode" + :minDate="dateTimePicker.min" :key="dateTimePicker.type" @confirm="fnDateTimePickerConfirm" + @cancel="fnDateTimePickerCancel"/> + <other-select :visible.sync="otherAssignmentsShow" v-model="form.SPECIAL_WORK" type="assignments"/> + <other-select :visible.sync="otherIdentificationShow" v-model="form.RISK_IDENTIFICATION" type="identification"/> + </view> </template> <script> - import { - getConfinedSpaceSelectList - } from '../../../api/index' - import OtherSelect from '@/components/other-select/index.vue'; +import {getConfinedSpaceSelectList} from '../../../api/index' +import OtherSelect from '@/components/other-select/index.vue'; +import {setSubmitForm} from "@/utils/submitHomeworkProcess"; - export default { - components: { - OtherSelect - }, - data() { - return { - form: { - APPLY_DEPARTMENT_ID: this.$store.getters.getUserInfo.DEPARTMENT_ID, - APPLY_DEPARTMENT_NAME: this.$store.getters.getUserInfo.DEPARTMENT_NAME, - APPLY_USER_ID: this.$store.getters.getUserInfo.USER_ID, - APPLY_USER_NAME: this.$store.getters.getUserInfo.NAME, - BELONG_CORP: '', - LIMITSPACE_NAME: '', - LIMITSPACE_ID: '', - MEDIUM_NAME: '', - WORK_CONTENT: '', - WORK_START_DATE: '', - WORK_END_DATE: '', - WORK_LONGITUDE: '', - WORK_LATITUDE: '', - WORK_USER: '', - SPECIAL_WORK: '', - }, - rules: {}, - picker: { - show: false, - columns: [], - key: 'NAME', - type: '' - }, - datetimePicker: { - show: false, - value: Number(new Date()), - min: new Date().getTime(), - mode: 'datetime', - type: '' - }, - otherAssignmentsShow: false, - otherIdentificationShow: false, - } - }, - onLoad(options) { - this.form.CORP_ID = options.CORPINFO_ID - this.form.CORP_NAME = options.CORP_NAME - }, - methods: { - async fnGetConfinedSpaceSelectList() { - const resData = await getConfinedSpaceSelectList() - this.picker.columns = [resData.varList] - }, - fnSingleChoiceClick(event) { - this.picker.type = event - if (event === 'LIMITSPACE_NAME') this.fnGetConfinedSpaceSelectList() - this.picker.show = true - }, - fnSingleChoiceConfirm(event) { - if (this.picker.type === 'LIMITSPACE_NAME') { - this.form.LIMITSPACE_ID = event.value[0].LIMITSPACE_ID - this.form.LIMITSPACE_NAME = event.value[0].NAME - } - this.fnSingleChoiceCancel() - }, - fnSingleChoiceCancel() { - this.picker.show = false - }, - fnDateTimePickerClick(event) { - this.datetimePicker.type = event - this.datetimePicker.value = Number(new Date(this.form[event])) || Number(new Date()) - this.datetimePicker.show = true - }, - fnDateTimePickerConfirm(event) { - this.form[this.datetimePicker.type] = uni.$u.timeFormat(event.value, 'yyyy-mm-dd hh:MM') - this.fnDateTimePickerCancel() - }, - fnDateTimePickerCancel() { - this.datetimePicker.show = false - }, - fnLocation() { - uni.navigateTo({ - url: '/pages/map/index', - events: { - acceptLocationData: (event) => { - this.form.WORK_LONGITUDE = event.data.longitue; - this.form.WORK_LATITUDE = event.data.latitude; - } - }, - }) - } - } - } +export default { + components: { + OtherSelect + }, + data() { + return { + formItems: [ + {name: '申请单位', key_name: 'APPLY_DEPARTMENT_NAME', key_id: 'APPLY_DEPARTMENT_ID', type: 0}, + {name: '申请人', key_name: 'APPLY_USER_NAME', key_id: 'APPLY_USER_ID', type: 0}, + {name: '作业分公司', key_name: 'CORP_NAME', key_id: 'CORP_ID', type: 0}, + {name: '受限空间所属单位', key_name: 'BELONG_CORP', type: 0}, + {name: '受限空间名称', key_name: 'LIMITSPACE_NAME', key_id: 'LIMITSPACE_ID', type: 0}, + {name: '受限空间内原有介质名称', key_name: 'MEDIUM_NAME', type: 0}, + {name: '作业内容', key_name: 'WORK_CONTENT', type: 0}, + {name: '作业开始时间', key_name: 'WORK_START_DATE', type: 0}, + {name: '作业结束时间', key_name: 'WORK_END_DATE', type: 0}, + {name: '经度', key_name: 'WORK_LONGITUDE', type: 0}, + {name: '纬度', key_name: 'WORK_LATITUDE', type: 0}, + {name: '作业人', key_name: 'WORK_USER', type: 0}, + {name: '关联的其他特殊作业及安全作业票编号', key_name: 'SPECIAL_WORK', type: 0}, + {name: '风险辨识结果', key_name: 'RISK_IDENTIFICATION', type: 0}, + ], + form: { + APPLY_DEPARTMENT_ID: this.$store.getters.getUserInfo.DEPARTMENT_ID, + APPLY_DEPARTMENT_NAME: this.$store.getters.getUserInfo.DEPARTMENT_NAME, + APPLY_USER_ID: this.$store.getters.getUserInfo.USER_ID, + APPLY_USER_NAME: this.$store.getters.getUserInfo.NAME, + CORP_ID: '', + CORP_NAME: '', + BELONG_CORP: '', + LIMITSPACE_NAME: '', + LIMITSPACE_ID: '', + MEDIUM_NAME: '', + WORK_CONTENT: '', + WORK_START_DATE: '', + WORK_END_DATE: '', + WORK_LONGITUDE: '1', + WORK_LATITUDE: '2', + WORK_USER: '', + SPECIAL_WORK: '', + RISK_IDENTIFICATION: '', + }, + rules: { + APPLY_DEPARTMENT_ID: [{type: 'string', required: true, message: '请选择申请单位', trigger: 'blur'}], + APPLY_DEPARTMENT_NAME: [{type: 'string', required: true, message: '请选择申请人', trigger: 'blur'}], + APPLY_USER_NAME: [{type: 'string', required: true, message: '请选择申请人', trigger: 'blur'}], + CORP_NAME: [{type: 'string', required: true, message: '请选择作业分公司', trigger: 'blur'}], + BELONG_CORP: [{type: 'string', required: true, message: '请输入受限空间所属单位', trigger: 'blur'}], + // LIMITSPACE_NAME: [{type: 'string', required: true, message: '请选择受限空间名称', trigger: 'blur'}], + MEDIUM_NAME: [{type: 'string', required: true, message: '请输入受限空间内原有介质名称', trigger: 'blur'}], + WORK_CONTENT: [{type: 'string', required: true, message: '请输入作业内容', trigger: 'blur'}], + WORK_START_DATE: [{type: 'string', required: true, message: '请选择作业开始时间', trigger: 'blur'}], + WORK_END_DATE: [{type: 'string', required: true, message: '请选择作业结束时间', trigger: 'blur'}], + WORK_LONGITUDE: [{type: 'string', required: true, message: '请选择经度', trigger: 'blur'}], + WORK_LATITUDE: [{type: 'string', required: true, message: '请选择纬度', trigger: 'blur'}], + WORK_USER: [{type: 'string', required: true, message: '请输入作业人', trigger: 'blur'}], + SPECIAL_WORK: [{ + type: 'string', + required: true, + message: '请输入关联的其他特殊作业及安全作业票编号', + trigger: 'blur' + }], + RISK_IDENTIFICATION: [{type: 'string', required: true, message: '请输入风险辨识结果', trigger: 'blur'}], + }, + picker: { + show: false, + columns: [], + key: 'NAME', + type: 'picker' + }, + dateTimePicker: { + show: false, + value: Number(new Date()), + min: new Date().getTime(), + mode: 'datetime', + type: 'datetime-picker' + }, + otherAssignmentsShow: false, + otherIdentificationShow: false, + } + }, + onLoad(options) { + this.form.CORP_ID = options.CORPINFO_ID + this.form.CORP_NAME = options.CORP_NAME + }, + methods: { + async fnGetConfinedSpaceSelectList() { + const resData = await getConfinedSpaceSelectList({TYPE: 2}) + this.picker.columns = [resData.list.list] + }, + fnSingleChoiceClick(event) { + this.picker.type = event + if (event === 'LIMITSPACE_NAME') this.fnGetConfinedSpaceSelectList() + this.picker.show = true + }, + fnSingleChoiceConfirm(event) { + if (this.picker.type === 'LIMITSPACE_NAME') { + this.form.LIMITSPACE_ID = event.value[0].LIMITSPACE_ID + this.form.LIMITSPACE_NAME = event.value[0].NAME + } + this.fnSingleChoiceCancel() + }, + fnSingleChoiceCancel() { + this.picker.show = false + }, + fnDateTimePickerClick(event) { + this.dateTimePicker.type = event + this.dateTimePicker.value = Number(new Date(this.form[event])) || Number(new Date()) + this.dateTimePicker.show = true + }, + fnDateTimePickerConfirm(event) { + this.form[this.dateTimePicker.type] = uni.$u.timeFormat(event.value, 'yyyy-mm-dd hh:MM') + this.fnDateTimePickerCancel() + }, + fnDateTimePickerCancel() { + this.dateTimePicker.show = false + }, + fnLocation() { + uni.navigateTo({ + url: '/pages/map/index', + events: { + acceptLocationData: (event) => { + this.form.WORK_LONGITUDE = event.data.longitue; + this.form.WORK_LATITUDE = event.data.latitude; + } + }, + }) + }, + async fnSubmit() { + try { + await this.$refs.formRef.validate() + try { + await setSubmitForm({ + form: this.form, + formItems: this.formItems, + TYPE: 'confinedSpace', + CORP_ID: this.form.CORP_ID + }) + } catch { + } + } catch { + uni.$u.toast('请补全必填项') + } + } + } +} </script> <style> -</style> \ No newline at end of file +</style> diff --git a/pages/eight_assignments/confined_space/index.vue b/pages/eight_assignments/confined_space/index.vue index 0d9f0eb..d111ff0 100644 --- a/pages/eight_assignments/confined_space/index.vue +++ b/pages/eight_assignments/confined_space/index.vue @@ -19,20 +19,20 @@ return { baseList: [ { - img: require('../../../static/icon-apps/app_icons.png'), + img: require('../../../static/icon-apps/i9.png'), title: '申请', - url: '/pages/selete_corp_info/index', + url: '/pages/eight_assignments/select_corp_info', params: { type: 'confined_space' } }, { - img: require('../../../static/icon-apps/app_icons.png'), + img: require('../../../static/icon-apps/i10.png'), title: '待办', url: '/pages/eight_assignments/confined_space/index' }, { - img: require('../../../static/icon-apps/app_icons.png'), + img: require('../../../static/icon-apps/i11.png'), title: '已办', url: '/pages/eight_assignments/confined_space/index' }, @@ -63,8 +63,8 @@ margin-top: 20upx; image { - width: 136upx; - height: 136upx; + width: 110upx; + height: 110upx; } .text { @@ -74,4 +74,4 @@ } } } -</style> \ No newline at end of file +</style> diff --git a/pages/eight_assignments/index.vue b/pages/eight_assignments/index.vue index 4e52857..e4322d8 100644 --- a/pages/eight_assignments/index.vue +++ b/pages/eight_assignments/index.vue @@ -19,42 +19,42 @@ return { baseList: [ { - img: require('../../static/icon-apps/app_icons.png'), + img: require('../../static/icon-apps/i1.png'), title: '受限空间安全作业', url: '/pages/eight_assignments/confined_space/index' }, { - img: require('../../static/icon-apps/app_icons.png'), + img: require('../../static/icon-apps/i2.png'), title: '盲板抽堵作业', url: '/pages/electronic_work_card/index' }, { - img: require('../../static/icon-apps/app_icons.png'), + img: require('../../static/icon-apps/i3.png'), title: '动土作业', url: '/pages/electronic_work_card/index' }, { - img: require('../../static/icon-apps/app_icons.png'), + img: require('../../static/icon-apps/i4.png'), title: '高处作业', url: '/pages/electronic_work_card/index' }, { - img: require('../../static/icon-apps/app_icons.png'), + img: require('../../static/icon-apps/i5.png'), title: '吊装作业', url: '/pages/electronic_work_card/index' }, { - img: require('../../static/icon-apps/app_icons.png'), + img: require('../../static/icon-apps/i6.png'), title: '临时用电作业', url: '/pages/electronic_work_card/index' }, { - img: require('../../static/icon-apps/app_icons.png'), + img: require('../../static/icon-apps/i7.png'), title: '动火作业', url: '/pages/electronic_work_card/index' }, { - img: require('../../static/icon-apps/app_icons.png'), + img: require('../../static/icon-apps/i8.png'), title: '断路作业', url: '/pages/electronic_work_card/index' }, @@ -84,8 +84,8 @@ margin-top: 20upx; image { - width: 136upx; - height: 136upx; + width: 110upx; + height: 110upx; } .text { @@ -95,4 +95,4 @@ } } } -</style> \ No newline at end of file +</style> diff --git a/pages/selete_corp_info/index.vue b/pages/eight_assignments/select_corp_info.vue similarity index 70% rename from pages/selete_corp_info/index.vue rename to pages/eight_assignments/select_corp_info.vue index 0019eac..63e2f03 100644 --- a/pages/selete_corp_info/index.vue +++ b/pages/eight_assignments/select_corp_info.vue @@ -1,9 +1,9 @@ <template> <view class="content"> <view class="card"> - <u-radio-group v-model="selete_corp" placement="column"> - <u-radio :customStyle="{marginBottom: '20upx'}" v-for="(item, index) in list" :key="item.CORPINFO_ID" - :label="item.CORP_NAME" :name="item.CORPINFO_ID"> + <u-radio-group v-model="select_corp" placement="column"> + <u-radio :customStyle="{marginBottom: '20rpx'}" v-for="item in list" :key="item.CORPINFO_ID" + :label="item.NAME" :name="item.CORPINFO_ID"> </u-radio> </u-radio-group> <view class="button_placeholder"></view> @@ -17,14 +17,14 @@ <script> import { getCorpInfoList - } from '../../api/index' + } from '../../api' export default { data() { return { type: '', list: [], - selete_corp: '' + select_corp: '' } }, onLoad(options) { @@ -34,10 +34,10 @@ methods: { async fnGetData() { const resData = await getCorpInfoList() - this.list = resData.data + this.list = resData.list.corpInfoDos }, fnConfirm() { - if (!this.selete_corp) { + if (!this.select_corp) { uni.$u.toast('请选择作业分公司') return; } @@ -47,13 +47,13 @@ 'default': '/pages/eight_assignments/confined_space/apply' } } - const url = urlByTypeMap[this.type][this.selete_corp] ?? urlByTypeMap[this.type].default + const url = urlByTypeMap[this.type][this.select_corp] ?? urlByTypeMap[this.type].default let params = {} for (let i = 0; i < this.list.length; i++) { - if (this.list[i].CORPINFO_ID === this.selete_corp) { + if (this.list[i].CORPINFO_ID === this.select_corp) { params = { CORPINFO_ID: this.list[i].CORPINFO_ID, - CORP_NAME: this.list[i].CORP_NAME, + CORP_NAME: this.list[i].NAME, } break } @@ -70,7 +70,7 @@ <style scoped lang="scss"> .button_placeholder { - height: 100upx; + height: 100rpx; } .button { @@ -79,4 +79,4 @@ left: 0; right: 0; } -</style> \ No newline at end of file +</style> diff --git a/pages/eight_assignments/select_department.vue b/pages/eight_assignments/select_department.vue new file mode 100644 index 0000000..9ba034c --- /dev/null +++ b/pages/eight_assignments/select_department.vue @@ -0,0 +1,88 @@ +<template> + <view> + <u-modal :show="modelShow" title="选择所属端" showCancelButton cancelText="返回" @cancel="fnModalCancel" + @confirm="fnModalConfirm"> + <u-radio-group v-model="type" placement="column"> + <u-radio :customStyle="{marginBottom: '8px'}" label="企业端" name="0"/> + <u-radio :customStyle="{marginBottom: '8px'}" label="监管端" name="1"/> + <u-radio :customStyle="{marginBottom: '8px'}" label="相关方端" name="2"/> + </u-radio-group> + </u-modal> + <next-tree + v-if="!modelShow" + uiMode="page" + ref="nextTreeRef" + checkStrictly + funcMode="radio" + selectParent + :ifSearch="false" + :treeData="treeData" + themeColor="#2a56f7" + page-height="92vh" + labelKey="DEPARTMENT_NAME" + valueKey="DEPARTMENT_ID" + childrenKey="nodes" + @change="fnTreeChange" + > + <template #fixedBottomBar> + <view style="padding: 20upx;margin-bottom: 20upx;"> + <u-button type="primary" text="确定" @click="fnTreeConfirm"/> + </view> + </template> + </next-tree> + </view> +</template> + +<script> +import {getDepartmentTree} from "@/api"; + +export default { + data() { + return { + modelShow: true, + type: '', + CORP_ID: '', + treeData:[], + treeSelectData:[] + } + }, + onLoad(query) { + this.CORP_ID = query.CORP_ID + }, + methods: { + fnModalCancel() { + uni.navigateBack() + }, + fnModalConfirm() { + if (!this.type) { + uni.$u.toast('请选择所属端') + return + } + this.modelShow = false + this.fnGetDepartmentTree() + }, + async fnGetDepartmentTree() { + let CORP_ID = '' + if (this.type === '0') CORP_ID = this.CORP_ID + if (this.type === '2') CORP_ID = this.$store.getters.getUserInfo.CORPINFO_ID + let resData = await getDepartmentTree({TYPE: this.type, CORP_ID}) + this.treeData = resData.tree.tree + }, + fnTreeChange(event){ + this.treeSelectData = event + }, + fnTreeConfirm(){ + this.getOpenerEventChannel().emit("confirm", { + DEPARTMENT_NAME: this.treeSelectData[0].DEPARTMENT_NAME, + DEPARTMENT_ID: this.treeSelectData[0].DEPARTMENT_ID, + TYPE: this.type + }); + uni.navigateBack(); + } + }, +} +</script> + +<style scoped lang="scss"> + +</style> diff --git a/pages/eight_assignments/select_review_personnel.vue b/pages/eight_assignments/select_review_personnel.vue new file mode 100644 index 0000000..59d5def --- /dev/null +++ b/pages/eight_assignments/select_review_personnel.vue @@ -0,0 +1,131 @@ +<template> + <view> + <view class="content"> + <view class="card"> + <u-form labelPosition="left" :model="form" :rules="rules" ref="formRef" labelWidth="140px"> + <u-form-item v-for="(item, index) in list" :key="index" :label="item.name" :prop="item.key_name" + borderBottom :required="item.isRequired === '1'" @click="fnSelect(item)"> + <u-input v-model="form[item.key_name]" border="none" readonly/> + <u-icon name="arrow-right"></u-icon> + </u-form-item> + </u-form> + <view class="mt-10"> + <u-button type="primary" text="下一步" @click="$u.debounce(fnSubmit, 1000,true)"/> + </view> + </view> + </view> + <u-picker :show="picker.show" :columns="picker.columns" keyName="NAME" + :key="picker.currentColumnKey" @confirm="fnPickerConfirm" @cancel="fnPickerCancel"/> + </view> +</template> + +<script> +import {getUserList} from "@/api"; +import {setPersonnelForm} from "@/utils/submitHomeworkProcess"; + +export default { + data() { + return { + taskId: '', + CORP_ID: '', + TYPE: '', + list: [], + form: {}, + rules: {}, + picker: { + show: false, + columnsAll: {}, + columns: [], + currentColumnKey: '', + currentName: '', + currentId: '', + } + } + }, + onLoad() { + const eventChannel = this.getOpenerEventChannel(); + eventChannel.on('nextNodes', (data) => { + this.list = data.nextNodes + this.taskId = data.taskId + this.CORP_ID = data.CORP_ID + this.TYPE = data.TYPE + this.fnInitFormAndRules() + }) + }, + methods: { + fnInitFormAndRules() { + this.list.forEach((item) => { + this.$set(this.form, item.key_name, '') + this.$set(this.form, item.key_id, '') + this.$set(this.rules, item.key_name, [{ + required: item.isRequired === '1', + message: item.name + '不能为空', + trigger: 'blur', + }]) + }) + }, + fnSelect({key_name, key_id, type, value}) { + if (type === '1') { + this.fnSelectDepartment(key_name, key_id, value) + return + } + if (type === '0') { + // 弹出选择人员 + this.fnSelectUser(key_name, key_id, value) + } + }, + fnSelectDepartment(key_name, key_id, value) { + uni.navigateTo({ + url: `/pages/eight_assignments/select_department?CORP_ID=${this.CORP_ID}`, + events: { + confirm: (event) => { + this.form[key_name] = event.DEPARTMENT_NAME + this.form[key_id] = event.DEPARTMENT_ID + this.form[value + '_USER_ID'] = '' + this.form[value + '_USER_NAME'] = '' + this.fnGetUserList(event, value) + } + }, + }) + }, + async fnGetUserList({DEPARTMENT_ID, TYPE}, value) { + let resData = await getUserList({DEPARTMENT_ID, TYPE}) + this.$set(this.picker.columnsAll, value, [resData.list.list]) + }, + fnSelectUser(key_name, key_id, value) { + this.picker.currentName = key_name + this.picker.currentId = key_id + this.picker.currentColumnKey = value + this.picker.columns = this.picker.columnsAll[this.picker.currentColumnKey] + if (!this.picker.columnsAll[this.picker.currentColumnKey]) { + uni.$u.toast('没有选择部门或当前部门没有人员') + return + } + this.picker.show = true + }, + fnPickerConfirm(event) { + this.form[this.picker.currentId] = event.value[0].USER_ID + this.form[this.picker.currentName] = event.value[0].NAME + this.fnPickerCancel() + }, + fnPickerCancel() { + this.picker.show = false + }, + async fnSubmit() { + try { + await this.$refs.formRef.validate() + try { + await setPersonnelForm({form: this.form,list:this.list, taskId: this.taskId, CORP_ID: this.CORP_ID, TYPE: this.TYPE}) + } catch { + } + } catch { + uni.$u.toast('请补全必填项') + } + } + }, +} +</script> + +<style scoped lang="scss"> + +</style> diff --git a/pages/index/index.vue b/pages/index/index.vue index 300467b..2b4a8b1 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -16,14 +16,16 @@ </view> </view> <view class="home-apps"> - <view class="home-apps-item" v-for="(item,index) in baseList" :key="index" @click="fnNavigator(index)"> - <view class="home-apps-item-img"> - <image :src="item.img" mode=""></image> + <template v-for="(item,index) in baseList"> + <view class="home-apps-item" :key="index" v-if="fnIsShow(index)" @click="fnNavigator(index)"> + <view class="home-apps-item-img"> + <image :src="item.img" mode=""></image> + </view> + <view class="text"> + <text>{{ item.title }}</text> + </view> </view> - <view class="text"> - <text>{{ item.title }}</text> - </view> - </view> + </template> </view> <u-modal :show="updateVersion.modalShow" title="温馨提示" :showConfirmButton="updateVersion.showConfirmButton" :showCancelButton="updateVersion.showCancelButton" :confirmText="updateVersion.confirmText" :cancelText="updateVersion.cancelText" @@ -70,13 +72,18 @@ export default { url: '/pages/employed_by/index' }, { - img: require('../../static/icon-apps/app_icons5.png'), + img: require('../../static/icon-apps/app_icons6.png'), title: '高危作业', url: '/pages/eight_assignments/index' }, ], } }, + computed: { + userInfo() { + return this.$store.getters.getUserInfo + } + }, onLoad() { this.fnUpdateVersion(false) }, @@ -109,6 +116,10 @@ export default { } }); }, + fnIsShow(index){ + if(index === 5) return !!this.userInfo.CORPINFO_ID + return true + }, fnNavigator(e) { uni.$u.route({ url: this.baseList[e].url diff --git a/pages/login/login.vue b/pages/login/login.vue index 6dac197..3b27015 100644 --- a/pages/login/login.vue +++ b/pages/login/login.vue @@ -36,8 +36,8 @@ export default { return { publicKey: 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2zCyUYSD0pNrbtaYdvGfHfWoRV+fo/2N9O2PLLz/jZvMkigkq4eAq4JO+Ek0wDHI9WxP6iTSLYCHhIOs9CQTPvyldJFm8riZtQZlBTD8Plkb3rjrgwTqbBi3w3+HKYdkSvGFXJIdSOPbpXnj5BzN8vlVaybs24R/vpUzG9178lwIDAQAB', form: { - userName: '', - userPwd: '' + userName: '14730797906', + userPwd: 'Aa@123456789' } } }, diff --git a/static/icon-apps/app_icons6.png b/static/icon-apps/app_icons6.png new file mode 100644 index 0000000..14d581e Binary files /dev/null and b/static/icon-apps/app_icons6.png differ diff --git a/static/icon-apps/i1.png b/static/icon-apps/i1.png new file mode 100644 index 0000000..e554051 Binary files /dev/null and b/static/icon-apps/i1.png differ diff --git a/static/icon-apps/i10.png b/static/icon-apps/i10.png new file mode 100644 index 0000000..c80f00c Binary files /dev/null and b/static/icon-apps/i10.png differ diff --git a/static/icon-apps/i11.png b/static/icon-apps/i11.png new file mode 100644 index 0000000..b50446c Binary files /dev/null and b/static/icon-apps/i11.png differ diff --git a/static/icon-apps/i2.png b/static/icon-apps/i2.png new file mode 100644 index 0000000..700886b Binary files /dev/null and b/static/icon-apps/i2.png differ diff --git a/static/icon-apps/i3.png b/static/icon-apps/i3.png new file mode 100644 index 0000000..87f1ba6 Binary files /dev/null and b/static/icon-apps/i3.png differ diff --git a/static/icon-apps/i4.png b/static/icon-apps/i4.png new file mode 100644 index 0000000..5654535 Binary files /dev/null and b/static/icon-apps/i4.png differ diff --git a/static/icon-apps/i5.png b/static/icon-apps/i5.png new file mode 100644 index 0000000..bff4090 Binary files /dev/null and b/static/icon-apps/i5.png differ diff --git a/static/icon-apps/i6.png b/static/icon-apps/i6.png new file mode 100644 index 0000000..29ccb09 Binary files /dev/null and b/static/icon-apps/i6.png differ diff --git a/static/icon-apps/i7.png b/static/icon-apps/i7.png new file mode 100644 index 0000000..4bd9b37 Binary files /dev/null and b/static/icon-apps/i7.png differ diff --git a/static/icon-apps/i8.png b/static/icon-apps/i8.png new file mode 100644 index 0000000..993bf41 Binary files /dev/null and b/static/icon-apps/i8.png differ diff --git a/static/icon-apps/i9.png b/static/icon-apps/i9.png new file mode 100644 index 0000000..20e98f2 Binary files /dev/null and b/static/icon-apps/i9.png differ diff --git a/uni_modules/next-search-more/changelog.md b/uni_modules/next-search-more/changelog.md new file mode 100644 index 0000000..69512e2 --- /dev/null +++ b/uni_modules/next-search-more/changelog.md @@ -0,0 +1,20 @@ +## 1.0.9(2023-07-06) +增加clear时,同事发送input事件 +## 1.0.8(2023-06-16) +更新修复input事件 +## 1.0.7(2023-06-15) +增加输入事件 +## 1.0.6(2023-06-09) +增加demo +## 1.0.5(2023-06-09) +更新more插槽的使用说明 +## 1.0.4(2023-06-09) +修复mode=more模式下input事件 +## 1.0.3(2023-06-08) +更改模式 +## 1.0.2(2023-06-08) +更改说明文件模式 +## 1.0.1(2023-06-08) +更新说明 +## 1.0.0(2023-06-08) +初始化next-search-more diff --git a/uni_modules/next-search-more/components/next-search-more/next-search-more.vue b/uni_modules/next-search-more/components/next-search-more/next-search-more.vue new file mode 100644 index 0000000..47b4e4a --- /dev/null +++ b/uni_modules/next-search-more/components/next-search-more/next-search-more.vue @@ -0,0 +1,239 @@ +<template> + <view class="next-search-more"> + <view class="search" :style="{ backgroundColor: backgroundColor }"> + <view class="content" :style="{ 'border-radius': radius + 'px', border: border }"> + <view class="content-box" :class="{ center: mode === 'center' }"> + <text class="icon icon-search"></text> + <input class="input" :class="{ center: !active && mode === 'center' }" :focus="isFocus" :placeholder="placeholder" v-model="inputVal" @input="input" @focus="focus" @blur="blur" /> + <text v-if="isDelShow" class="icon icon-del" @click="clear"></text> + </view> + <view v-show="(active && isFixedSearchBtn && button === 'inside') || (isDelShow && button === 'inside')" class="searchBtn" @click="search">搜索</view> + </view> + <template v-if="mode === 'common' || mode ==='center'"> + <view v-if="button === 'outside'" class="button" :class="{ active: isFixedSearchBtn || active }" @click="search"> + <view class="button-item">{{ !isFixedSearchBtn ? searchName : '搜索' }}</view> + </view> + </template> + <template v-else-if="mode === 'more'"> + <view class="button active" @click="selectMore"> + <view class="button-item"><text class="icon icon-more"></text></view> + </view> + </template> + </view> + <view class="more-container-parent"> + <view v-if="mode === 'more' && showMore" class="more-container"> + <slot name="more"></slot> + </view> + </view> + </view> +</template> + +<script> +export default { + props: { + mode: { + type: String, + default: 'common' + }, + button: { + type: String, + default: 'outside' + }, + isFixedSearchBtn: { + type: Boolean, + default: true + }, + radius: { + type: String, + default: '60' + }, + placeholder: { + type: String, + default: '请输入搜索内容' + }, + backgroundColor: { + type: String, + default: '#fff' + }, + showMore: { + type: Boolean, + default: false + }, + border: { type: String, default: '1px #f5f5f5 solid' } + + }, + data() { + return { + active: false, + inputVal: '', + searchName: '取消', + isDelShow: false, + isFocus: false, + timer: 0 + }; + }, + methods: { + focus() { + this.active = true; + }, + blur() { + this.isFocus = false; + if (!this.inputVal) { + this.active = false; + } + }, + input() { + clearInterval(this.timer) + this.timer = setTimeout(() => { + this.$emit('input', this.inputVal); + }, 500) + }, + clear() { + this.inputVal = ''; + this.active = false; + this.$emit('input', this.inputVal); + this.$emit('search', ''); + }, + getFocus() { + this.isFocus = true; + }, + search() { + if (!this.inputVal) return; + this.$emit('search', this.inputVal); + }, + selectMore() { + this.$emit('moreClick') + } + }, + created() { + this.$watch(() => this.inputVal, (newVal) => { + if (newVal) { + this.searchName = '搜索'; + this.isDelShow = true; + } else { + this.searchName = '取消'; + this.isDelShow = false; + } + }) + } +}; +</script> + +<style lang="scss" scoped> +.next-search-more { + .search { + display: flex; + width: 100%; + border-bottom: 1px #f5f5f5 solid; + box-sizing: border-box; + padding: 15upx; + font-size: $uni-font-size-base; + background: #fff; + .content { + display: flex; + align-items: center; + width: 100%; + height: 60upx; + border: 1px #ccc solid; + background: #fff; + overflow: hidden; + transition: all 0.2s linear; + border-radius: 30px; + + .content-box { + width: 100%; + display: flex; + align-items: center; + &.center { + justify-content: center; + } + .icon { + padding: 0 15upx; + &.icon-del { + font-size: 38upx; + } + } + .input { + width: 100%; + max-width: 100%; + line-height: 60upx; + height: 60upx; + transition: all 0.2s linear; + &.center { + width: 200upx; + } + &.sub { + // position: absolute; + width: auto; + color: grey; + } + } + } + .searchBtn { + height: 100%; + flex-shrink: 0; + padding: 0 30upx; + background: $uni-color-success; + line-height: 60upx; + color: #fff; + border-left: 1px #ccc solid; + transition: all 0.3s; + } + } + + .button { + display: flex; + align-items: center; + justify-content: center; + position: relative; + flex-shrink: 0; + width: 0; + transition: all 0.2s linear; + white-space: nowrap; + overflow: hidden; + &.active { + padding-left: 15upx; + width: 100upx; + } + .icon-more { + font-size: 48upx; + } + } + } + .more-container-parent { + flex-shrink: 0; + width: 100%; + // position: fixed; + // position: sticky; + z-index: 997; + flex-wrap: nowrap; + display: flex; + flex-direction: row; + position: relative; + flex-direction: column; + .more-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: auto; + background-color: #ffffff; + padding: 20rpx; + border-radius: 0 0 30rpx 30rpx; + box-sizing: border-box; + overflow: hidden; + } + } +} + +@font-face { + font-family: 'iconfont'; + src: url('https://at.alicdn.com/t/c/font_4110624_nikfg21uyk8.ttf?t=1686190660183') format('truetype'); +} +.icon { + font-family: iconfont; + font-size: 32upx; + font-style: normal; + color: #999; +} +</style> diff --git a/uni_modules/next-search-more/package.json b/uni_modules/next-search-more/package.json new file mode 100644 index 0000000..f0f64e2 --- /dev/null +++ b/uni_modules/next-search-more/package.json @@ -0,0 +1,79 @@ +{ + "id": "next-search-more", + "displayName": "next-search-more(vue2 vue3多端通用)搜索框组合、搜索下拉框组件全端可用", + "version": "1.0.9", + "description": "搜索下拉框,支持多种展示形式,可以结合第三方组件一起使用,可自定义", + "keywords": [ + "远程搜索", + "组合搜索", + "配置搜索", + "自定义搜索", + "搜索下拉框" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.1" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "插件不采集任何数据", + "permissions": "无" + }, + "npmurl": "", + "type": "component-vue" + }, + "uni_modules": { + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "n", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "n", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "n", + "Edge": "n", + "Firefox": "n", + "Safari": "n" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u" + }, + "快应用": { + "华为": "n", + "联盟": "n" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/next-search-more/readme.md b/uni_modules/next-search-more/readme.md new file mode 100644 index 0000000..b418bae --- /dev/null +++ b/uni_modules/next-search-more/readme.md @@ -0,0 +1,215 @@ + +## next-search-more --搜索更多,搜索下拉,search-more + +> 遇到问题或有建议可以加入QQ群(<font color=#f00>455948571</font>)反馈 +> 如果觉得组件不错,<font color=#f00>给五星鼓励鼓励</font>咯! + + +## 使用 + +>[从uniapp插件市场导入](https://ext.dcloud.net.cn/plugin?name=next-search-more) + +```html +<template> + <view class="index"> + <view style="padding: 10px 0;background-color:#000000;color: #fff;font-size: 13px;"><text>1. mode=more模式</text></view> + <next-search-more mode="more" :showMore="showMore" @moreClick="moreClick"> + <!--以下demo你可以借助第三方的插件实现你想要的任何复杂功能--> + <template #more> + <u--form labelWidth="80" labelAlign="right"> + <u-form-item label="radio:"> + <u-radio-group v-model="searchForm.radio" placement="row"> + <u-radio v-for="(item, index) in optionslist" :key="index" :label="item.name" :name="item.name" /> + </u-radio-group> + </u-form-item> + <u-form-item label="checkbox:"> + <u-checkbox-group v-model="searchForm.checkbox" placement="row"> + <u-checkbox v-for="(item, index) in optionslist" :key="index" :label="item.name" :name="item.name" /> + </u-checkbox-group> + </u-form-item> + <u-form-item label="rate:"> + <u-rate :count="5" v-model="searchForm.rate" /> + </u-form-item> + <u-form-item label="switch:"> + <u-switch v-model="searchForm.switch"></u-switch> + </u-form-item> + <u-form-item label="other:"> + <next-search-select + :multiple="false" + :list="options" + label-key="projectName" + value-key="id" + placeholder=" 请选择报备项目" + title="选择报备项目" + v-model:value="searchForm.projectId" + @search="searchFunc" + @change="changeCallback" + clearable + ></next-search-select> + </u-form-item> + <view class="flex-row"> + <u-button @click="cancel" :customStyle="{margin: '10rpx'}" text="取消"></u-button> + <u-button @click="comfirm" :customStyle="{margin: '10rpx'}" type="primary" text="确定"></u-button> + </view> + </u--form> + </template> + </next-search-more> + <view style="padding: 10px 0;background-color:#000000;color: #fff;font-size: 13px;"><text>2. mode=common;button=outside模式</text></view> + <next-search-more mode="common" button="outside" /> + <view style="padding: 10px 0;background-color:#000000;color: #fff;font-size: 13px;"><text>3. mode=common;button=inside模式</text></view> + <next-search-more mode="common" button="inside" /> + <view style="padding: 10px 0;background-color:#000000;color: #fff;font-size: 13px;"><text>4. mode=center;button=outside模式</text></view> + <next-search-more mode="center" button="outside" /> + <view style="padding: 10px 0;background-color:#000000;color: #fff;font-size: 13px;"><text>5. mode=center;button=inside模式</text></view> + <next-search-more mode="center" button="inside" /> + <view style="padding: 10px 0;background-color:#000000;color: #fff;font-size: 13px;"><text>6. mode=center;button=inside;isFixedSearchBtn=false模式</text></view> + <next-search-more mode="center" button="inside" :isFixedSearchBtn="false" /> + <view class="content-block"><text>全站ICON图标海量下载iconfont图标大全,为你优选-包图网,全站ICON图标海量下载iconfont图标大全,为你优选-包图网</text></view> + </view> +</template> +``` + +### vue3 + ts 使用 + +```js +<script lang="ts"> +import { ref, nextTick, toRefs, toRaw, unref, reactive } from 'vue' + +export default { + setup() { + const showMore = ref(false) + const searchForm = reactive({ + radio: '', + checkbox: '', + rate: 3, + switch: false, + projectId: '' + }) + let dataLength = 0 + const options = ref<any>([]) + const optionslist = ref([{ + name: '苹果', + disabled: false + }, + { + name: '香蕉', + }, + { + name: '橙子', + } + ]) + function cancel () { + showMore.value = false + } + function comfirm () { + showMore.value = false + } + function moreClick () { + showMore.value = !unref(showMore) + } + function searchFunc(val?) { + console.log("搜索的关键字:", val) + uni.showLoading({ + title: '请稍后...', + icon: 'none' + }) + // 模拟ajax请求 + setTimeout(() => { + options.value = [] + dataLength = 0 + if (dataLength < 40) { + for (let i = 0; i < 40; i++) { + options.value.push({ + id: `id-${val ? val + '-' : ''}${dataLength + i}`, + projectName: `项目item-${val ? val + '-' : ''}${dataLength + i}`, + ohterKey: `test-${i}` + }) + } + dataLength = unref(options).length + } + uni.hideLoading() + }, 1000) + } + function changeCallback(item) { + console.log("选中的item:", item) + } + searchFunc() + return { + showMore, + moreClick, + optionslist, + searchForm, + searchFunc, + options, + changeCallback, + comfirm, + cancel + } + } +} +</script> +<style lang="scss"> + + .flex-row { + display: flex; + justify-content: space-around; + } + .content-block { + border-radius: 20rpx; + border: 1rpx solid #ccc; + margin: 20rpx; + padding: 20rpx; + } +</style> +<style lang="scss"> +page { + background: #ccc; +} +</style> + + +``` +### vue2 同样支持,在这里不再写demo + +### 组件按需加载 +如果不需要组件全局加载,而已把组件拷贝到项目的components目录下,单独引入进来使用即可达到按需加载的效果 +### 预览 + +*** + +| 功能预览 | | 项目中应用演示 | +| :--------------------------------------------------------------------------:| | :-----------------------------------------------------------------------------:| +|  | |  | + + +## 参数 + + +### next-search-more Props + +可选参数属性列表 + +|参数名 |说明 |类型 |是否必填 |默认值 |可选值 | +|---- |---- |---- |---- |---- |---- | +|mode |模式mode,支持common模式 center模式 more模式 |String |否 |common |center,more| +|button |搜索按钮的模式,支持outside模式 inside模式 |String |否 |outside |inside | +|isFixedSearchBtn |是否固定搜索按钮 |Boolean |否 |true |false | +|radius |搜索控件的radius |String, Number |否 |60 |- | +|placeholder | placeholder |String |否 |请输入搜索内容 |- | +|backgroundColor |搜索控件的背景颜色 |String |否 |#fff |- | +|showMore | mode=more模式下,用于控制打开下拉弹层 |Boolean |否 |false |true | +|border| border |String |否 |1px #f5f5f5 solid |- | + + +# Event 事件 +|事件名 |说明 |类型 |回调参数 | +|---- |---- |---- |---- | +|input |搜索框输入事件 |emit |- | +|search|搜索触发的事件 |emit |- | +|moreClick|更多按钮点击触发事件 |emit |- | + +## Slot 插槽 + +|名称 |说明 |参数 | +|---- |---- |---- | +|more |more插槽,在mode=more模式下用于存放下拉框内容 |无 | diff --git a/uni_modules/next-tree/changelog.md b/uni_modules/next-tree/changelog.md new file mode 100644 index 0000000..0e31f4b --- /dev/null +++ b/uni_modules/next-tree/changelog.md @@ -0,0 +1,173 @@ +## 1.8.9(2024-06-28) +完善说明文件 +## 1.8.8(2024-06-21) +更新插件 +## 1.8.7(2024-05-08) +fix bug +## 1.8.6(2024-04-07) +功能更新 +## 1.8.5(2024-04-07) +修复bug +## 1.8.3(2024-04-06) +update +## 1.8.2(2024-04-06) +update +## 1.8.1(2024-04-06) +fix bug +## 1.8.0(2024-04-03) +优化 +## 1.7.9(2024-04-03) +优化编辑tree功能 +## 1.7.8(2024-04-02) +超集功能上线 +## 1.7.7(2024-03-26) +增加expandedMode=singe +## 1.7.6(2024-02-27) +修复特殊字符的处理方法 +## 1.7.5(2024-02-26) +增加无子节点的父节点配置 +## 1.7.4(2024-02-05) +修复vue2,语法使用问题 +## 1.7.3(2024-02-05) +修复vue2,插槽不显示bug +## 1.7.2(2024-02-05) +优化大数据的性能 +## 1.7.1(2024-01-25) +优化功能说明 +## 1.7.0(2024-01-24) +增加expandedKeys默认展开项目,配置 +## 1.6.9(2024-01-23) +增加change事件 +## 1.6.8(2024-01-22) +搜索模式下,不再响应展开收起逻辑 +## 1.6.7(2024-01-04) +修复异步加载节点搜索展示bug +## 1.6.6(2023-12-29) +修复next-tree异步加载节点关闭bug +## 1.6.5(2023-12-21) +fix bug +## 1.6.4(2023-12-21) +fix bug +## 1.6.3(2023-12-21) +更新vue2版本说明文档 +## 1.6.2(2023-12-21) +修复说明文档 +## 1.6.1(2023-12-18) +增加empty插槽 +## 1.6.0(2023-12-18) +增加empty插槽 +## 1.5.9(2023-12-15) +修改说明文件 +## 1.5.8(2023-12-15) +修复changeVerify函数单选时,返回参数bug +## 1.5.7(2023-12-14) +修复checkStrictlyModel === 'strong'的bug +## 1.5.6(2023-12-13) +代码优化 +## 1.5.5(2023-12-13) +优化changeVerify的使用 +## 1.5.4(2023-12-12) +修复提示层级问题 +## 1.5.3(2023-12-12) +优化uiMode=page模式下的使用 +## 1.5.2(2023-12-11) +增加uiMode配置,实现页面模式展示 +## 1.5.1(2023-12-06) +更新说明 +## 1.5.0(2023-12-06) +更新插件使用注意事项 +## 1.4.9(2023-12-01) +增加topBar插槽 +## 1.4.8(2023-11-30) +增加changeVerify验证函数,实现change的各种控制 +## 1.4.7(2023-11-30) +增加弹层容器高度可配置 +## 1.4.6(2023-11-28) +修复bug +## 1.4.5(2023-11-28) +修复disabled是,需要显示灰色不可操作 +## 1.4.4(2023-11-28) +增加说明 +## 1.4.3(2023-11-28) +增加主题配置 +## 1.4.2(2023-11-28) +增加异步加载时,子节点说明 +## 1.4.1(2023-11-27) +修复说明bug +## 1.4.0(2023-11-27) +next-tree 全面说明文档 +## 1.3.6(2023-11-27) +增加远程加载loadData,全面实现全功能覆盖 +## 1.3.5(2023-11-27) +增加title的定义 +## 1.3.4(2023-11-27) +增加title支持自定义定制 +## 1.3.3(2023-11-21) +增加搜索模式searchModel=depHighlight模式,从属高亮显示模式 +## 1.3.2(2023-11-20) +修复valueKey设置bug +## 1.3.1(2023-11-17) +增加说明文件,和demo +## 1.3.0(2023-11-17) +修复clear时不支持关联模式的设置 +## 1.2.9(2023-11-17) +增加checkStrictlyModel模式设置,强关联,和弱关联 +## 1.2.8(2023-11-16) +增加next-tree的辅助线模式 +## 1.2.7(2023-11-16) +优化next-tree +## 1.2.6(2023-11-16) +修复搜索时,隐藏未打开的数据 +## 1.2.5(2023-11-16) +修复搜索无法点击,和级联半选不生效问题 +## 1.2.4(2023-11-16) +更新新功能插件使用说明 +## 1.2.3(2023-11-16) +增加插槽模式,只是高ui要求定制 +## 1.2.2(2023-11-15) +修复checkStrictly配置下,子关联父的选择状态 +## 1.2.1(2023-11-15) +增加半选提示功能配置showHalfCheckedTips +## 1.2.0(2023-11-14) +修复disabled配置状态下,父子级联,不需要改变disabled设置项的选择状态 +## 1.1.9(2023-11-13) +增强大数据量体验交互,增加筛选搜索模式 +## 1.1.8(2023-11-13) +增加清除clear和取消cancel事件 +## 1.1.7(2023-11-08) +更新next-tree插件功能清单说明 +## 1.1.6(2023-11-07) +update说明文档 +## 1.1.5(2023-11-07) +update +## 1.1.4(2023-11-07) +更新readme.md说明 +## 1.1.3(2023-11-07) +更新说明demo +## 1.1.2(2023-11-07) +增加子节点按需渲染演示demo +## 1.1.1(2023-11-07) +增加清空功能 +## 1.1.0(2023-11-07) +增加子孙节点按需渲染,扩展本插件支持大数据量渲染; +## 1.0.9(2023-10-26) +增加文件说明 +## 1.0.8(2023-09-14) +增加禁用节点属性配置disabledKey +## 1.0.7(2023-09-06) +增加checkStrictly,实现父子节点关联 +## 1.0.6(2023-09-06) +更新vue2使用过程视图不更新的技术说明 +## 1.0.5(2023-09-06) +修复说明文档 +## 1.0.4(2023-06-19) +修改demo +## 1.0.3(2023-06-19) +更新vue2的使用demo +## 1.0.2(2023-06-19) +修复说明文档 + +## 1.0.1(2023-05-10) +更新说明文件 +## 1.0.0(2023-05-09) +初始化项目 diff --git a/uni_modules/next-tree/components/next-tree/next-tree.vue b/uni_modules/next-tree/components/next-tree/next-tree.vue new file mode 100644 index 0000000..6a65f6c --- /dev/null +++ b/uni_modules/next-tree/components/next-tree/next-tree.vue @@ -0,0 +1,1043 @@ +<template> + <view class="next-tree"> + <view class="next-tree-mask" v-if="uiMode === 'popup'" :class="{'show':showTree}" @tap="_cancel"></view> + <view class="next-tree-cnt" :style="{top: top, height: pageHeight}" :class="{'show':showTree, 'next-tree-cnt-page': uiMode === 'page'}"> + <view v-if="_showTreeBar" class="next-tree-bar"> + <view class="next-tree-bar-cancel" :style="{'color':cancelColor}" hover-class="hover-c" @tap="_cancel">取消</view> + <view class="next-tree-bar-title" :style="{'color':titleColor}">{{customTitle}}</view> + <view class="next-tree-bar-btns"> + <view v-if="['checkbox', 'radio'].indexOf(funcMode) !== -1" class="next-tree-bar-cancel" :style="{'color':cancelColor}" hover-class="hover-c" @tap="_clear">清空</view> + <view v-else-if="funcMode === 'edit'" class="next-tree-bar-cancel" :style="{'color':cancelColor}" hover-class="hover-c" @tap="_revert">还原</view> + <view class="btn-divid"></view> + <view class="next-tree-bar-confirm" :style="{'color':_themeColor}" hover-class="hover-c" @tap="_confirm">确定</view> + </view> + </view> + <!-- #ifdef APP-PLUS--> + <view class="next-tree-view" :style="'top:'+(_showTreeBar?'72rpx':'0rpx')"> + <!-- #endif--> + <!-- #ifdef H5--> + <view class="next-tree-view" style="top: 72rpx"> + <!-- #endif--> + <next-search-more v-if="ifSearch" @search="onSearch" mode="center" placeholder="请输入关键字" :isFixedSearchBtn="false" /> + <slot name="topBar"></slot> + <scroll-view class="next-tree-view-sc" :scroll-y="true"> + <view v-if="_treeList.length"> + <block v-for="(item, index) in _treeList" :key="index"> + <view class="next-tree-item-block" v-if="item.show"> + <view class="next-tree-item" :style="[{ + paddingLeft: item.rank*15 + 'px', + zIndex: item.rank*-1 +50 + }]" + :class="{ + border: border === true, + show: item.show, + last: item.lastRank, + showchild: item.showChild, + open: item.open, + disabled: item.disabled === true + }"> + <block v-if="showAuxiliaryLine"> + <template v-if="item.rank > 1"> + <view :key="i" v-for="i in (item.rank - 1)" :style="{left: (6 * (2*i - 1) + 3 * (i - 1)) + 'px'}" class="parent-horizontal-line"></view> + </template> + <view class="left-line"> + <view v-if="item.lastRank" class="horizontal-line"></view> + </view> + </block> + <view class="next-tree-label" @tap.stop="_treeItemTap(item, index)"> + <image class="next-tree-icon" :src="item.lastRank ? lastIcon : item.showChild ? currentIcon : defaultIcon"></image> + <input @click.stop @tap.stop class="label-input" placeholder="请输入" v-if="funcMode === 'edit' && item.status === 'edit'" v-model="item.name" /> + <rich-text :nodes="getNodes(item.ouputText)" :selectable="false" v-else-if="ifSearch && searchModel==='depHighlight' && keywords"></rich-text> + <slot v-else-if="$slots.label" name="label" :data="_getLabelSlotData(item)"></slot> + <rich-text v-else-if="item.checked && !item.disabled" :nodes="getThemeNodes(item.name)"></rich-text> + <text v-else>{{item.name}}</text> + </view> + <template v-if="['checkbox', 'radio'].indexOf(funcMode) !== -1"> + <view class="next-tree-check" @tap.stop="_treeItemSelect(item, index)" v-if="selectParent === true ? true : item.lastRank"> + <view class="next-tree-check-yes" v-if="item.checked" :class="{'radio':!multiple}" :style="{'border-color': item.disabled ? '#ccc' : _themeColor, 'background-color': item.disabled ? '#ccc' : _themeColor}"> + <view class="next-tree-check-yes-b" :style="{'background-color':item.disabled ? '#ccc' : _themeColor}"> + <text v-if="item.checked" class="icon-text">✔</text> + </view> + </view> + <view class="next-tree-check-no" v-else :class="{'radio':!multiple}" :style="{'border-color': item.disabled ? '#ccc' : _themeColor}"> + <text v-if="showHalfChecked(item) && showHalfCheckedTips" :style="{'color': item.disabled ? '#ccc' : _themeColor, 'font-weight': 'blod', 'font-size': '10px'}" class="icon-text">一</text> + </view> + </view> + </template> + <template v-else-if="funcMode === 'edit'"> + <text v-if="item.status === 'loading'" :style="{'color': '#ccc'}" class="iconfont-loading icon-btn"></text> + <view v-else> + <text @click="_complete(item)" v-if="item.status === 'edit'" :style="{'color': item.disabled ? '#ccc' : _themeColor}" class="iconfont icon-btn"></text> + <template v-else> + <text @click="_editItem(item)" :style="{'color': item.disabled ? '#ccc' : _themeColor}" class="iconfont icon-btn"></text> + <text @click="_addSubItem(item)" :style="{'color': item.disabled ? '#ccc' : '#333'}" class="iconfont icon-btn"></text> + <text @click="_addSameItem(item)" :style="{'color': item.disabled ? '#ccc' : '#333'}" class="iconfont icon-btn"></text> + </template> + <text @click="_delItem(item)" :style="{'color': item.disabled ? '#ccc' : '#ff4d4f'}" class="iconfont icon-btn"></text> + </view> + + </template> + </view> + </view> + </block> + </view> + <view v-else> + <slot v-if="$slots.empty" name="empty"></slot> + <view class="empty" v-else><text>暂无数据</text></view> + </view> + <view v-if="ifSearch" style="height: 80rpx"></view> + <slot name="bottomBar"></slot> + </scroll-view> + </view> + </view> + <view class="fixed-bottom-bar"><slot name="fixedBottomBar"></slot></view> + </view> +</template> + +<script> +export default { + name: "next-tree", + props: { + uiMode: { + type: String, + default: 'popup' // popup(弹窗), page(页面) + }, + funcMode: { + type: String, + default: 'radio' // display(展示模式), edit(编辑模式), checkbox(多选模式), radio(单选模式) + }, + treeData: { + type: Array, + default: function() { + return [] + } + }, + valueKey: { + type: String, + default: 'id' + }, + labelKey: { + type: String, + default: 'label' + }, + disabledKey: { + type: String, + default: 'disabled' + }, + childrenKey: { + type: String, + default: 'children' + }, + title: { + type: [String, Function], + default: '' + }, + selectParent: { //是否可以选父级 + type: Boolean, + default: false + }, + foldAll: { //折叠时关闭所有已经打开的子集,再次打开时需要一级一级打开 + type: Boolean, + default: false + }, + themeColor: { // 主题颜色 + type: String, + default: '#f9ae3d' // #f9ae3d + }, + cancelColor: { // 取消按钮颜色 + type: String, + default: '' // #757575 + }, + titleColor: { // 标题颜色 + type: String, + default: '' // #757575 + }, + currentIcon: { // 展开时候的ic + type: String, + default: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MEQ0QTM0MzQ1Q0RBMTFFOUE0MjY4NzI1Njc1RjI1ODIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MEQ0QTM0MzU1Q0RBMTFFOUE0MjY4NzI1Njc1RjI1ODIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowRDRBMzQzMjVDREExMUU5QTQyNjg3MjU2NzVGMjU4MiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowRDRBMzQzMzVDREExMUU5QTQyNjg3MjU2NzVGMjU4MiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PidwepsAAAK0SURBVHja7JxbTsJAFIYHww7ciStgCeoGvGxAiOsgURegoL5720AXYLiIr0aJviq3Zx3PhIEnKG3ndtr+f3KixrSUj/ZjzjClIqUUiFm2gAAQAREQEUAEREAERAQQAREQAREBREAEREBEEqa67h9RFDWllDv0awWYlqlQHmu1WjMRRMoV1QFttA12y3xRtdNczq8EsE4/f8FumX2q77ROvNXk8UGMEKdUz6tYJHljaZAbuyUH+UR1to5BEohTuqwPCeS4pAA/qY6o/kyHOAMCeRK3owJnj+rH1jjxhqpVsstaebCz6TmnHWyXyY+xHjSBWBY/bvSgadtXBj9u9KCN3rnIfkzkQVsTEEX0Y2IP2oKo/HhMICcFAThUcwVZNGU6FdbX/XURzkbVF4+ybGhjPrFdgP66QdXNurGtSdk6Xdb9nAJ8oDo3OQlsQZzkdPw41ONBo6vI5scDefRjZg+6gpg3Pxp50CXEvPjR2IOuIXL3oxUPuobI3Y9WPOgDIlc/WvOgL4iL/vqFCcD7LH0xB4hj7cfQ/fWH9qCT+FhG0tN+DBk1PzjOM0SVllixcsBT1AvYc/kAPhc0hRg/3uvxoCgKRN9+dOrBUBB9+9GpB0NC9OVH5x4MDdG1H714kANEV3705kEOEBf9dcPi/lQnsuvLg1wgSu3Ha0v7Uh4MMgUXeuG71H407a+VBy9CPQkOdw+MtB+nGbd/D+FBbhBNxo9SjwcngJjNj0E9yBFiFj8G9SBXiGn8GNyDnCEm8SMLD3KHGOdHNh7kDjHOj2w8mAeIi/5arX+c6b/fxHz9oADEdGdjR/fXCw/OOB5oVfCOgnepz8IB14PMw03jCmTE+QBx5z0gAmKSqK9OUF+hcAeIhu/QYr4Qie8rjW83hhMBERARQAREQAREBBABERCLnH8BBgA+TQI7U4t53AAAAABJRU5ErkJggg==' + }, + defaultIcon: { // 折叠时候的ic + type: String, + default: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAACE0lEQVR4Xu3c200DMRCF4XEltJAOkEugA+ggpUAHoQMqiFMCdEAJUMEiS4mEELlIO7bPOeN9i6K1rG/952myyea1WiCtXmEuYBPR4RBMxInoIOCwhOtJLKVszWyXc/5y2BvNEq6I+/3+kFK6M7OHnPM7jcLKjbZAvD/uaZtzflm5P4rbWyJWgDcze1LPuzVihfxUz7sH4ilJ2bx7Isrm3RtRMu8RiHJ5j0SUyXs0okTeCIj0eSMh0uaNhkiZNyIiXd7IiDR5oyNS5M2ACJ83EyJs3myIkHkzIsLlzYwIkzc7IkTeCojD81ZCHJa3GuKQvBURu+etjNgtb3XELnlHQGyedyTEZnlHQ2ySd0RE97wjI7rlHR3RJe+JeIrbLOecD6ePpZQ6W1kn2epo4MUrPOKyLN8ppYq1+y1VStncOjIdGnFZlo+U0uOtWOeOY2TE12Ouq//pEA7xXL7XfvcufR8K0Svfv6CREN3yDYfYIt9QiK3yjYTYLF95xB75SiP2ylcZsVu+cogj8pVCHJWvEuKwfOkREfKlRkTJlxkRJl86RMR8qRBR82VChM0XHpEhX2hElnyREWnyhUNkzBcKkTVfJETafIcjKuQ7FFEl35GIMvl2R1TMtyuiar49EWXzbY5oZpv/hibXTF2h3+s60FRKeT6+3TjMS3nrA3ZFRD8xrfY3ER1kJ+JEdBBwWGKeRAfEH1wS5WFZSDB/AAAAAElFTkSuQmCC' + }, + lastIcon: { // 没有子集的ic + type: String, + default: '' + }, + border: { // 是否有分割线 + type: Boolean, + default: false + }, + checkStrictly: { // 只有在funcMode为checkbox状态下生效; 状态下节点选择完全受控(父子节点选中状态不再关联) + type: Boolean, + default: false + }, + checkStrictlyModel: { // 关联模式 weak: 弱关联;strong: 强关联 + type: String, + default: 'weak' + }, + showHalfCheckedTips: { // 只有在funcMode为checkbox, checkStrictly为false状态下生效; 父子节点选中状态不再关联,显示半选提示 + type: Boolean, + default: true + }, + ifSearch: { // 是否开启search模式 + type:Boolean, + default: true + }, + searchModel: { // 搜索模式配置 + type: String, + default:'common' // depHighlight: 从属高亮;common: 一般;remote:远程 + }, + showAuxiliaryLine: { // 辅助线模式 + type:Boolean, + default: false + }, + loadData: { + type: Function + }, + height: { + type: Number, + default: 500 + }, + changeVerify: { + type: Function + }, + expandedKeys: { // (Controlled) Specifies the keys of the expanded treeNodes + type: Array, + default: () => ([]) + }, + expandedMode: { // common: 一般模式;singe: 单一模式; + type: String, + default: 'common' + }, + pageHeight:{ + type: String, + default: '100%' + }, + }, + data() { + return { + showTree: false, + treeList: [], + currentTreeData: [], + selectIndex: -1, + keywords: '', + nodeInitContrl: {}, + top: '', + initNum: 1 + } + }, + computed: { + _showTreeBar() { + return this.uiMode === 'popup' + }, + _themeColor() { + return this.themeColor || '#f9ae3d' + }, + _treeList () { + if(this.ifSearch && this.keywords) { + return this.treeList.filter(item => { + return (item.name && item.name.indexOf(this.keywords) !== -1) + }).map(item => { + const o = JSON.parse(JSON.stringify(item)); + if(o.showChild === false) { + o.showChild = true; + } + if(o.show === false) { + o.show = true; + } + return o + }) + } else { + return this.treeList + } + }, + customTitle() { + if(typeof this.title === 'function') { + return this.title(this._getCheckedParams()); + } else { + return this.title + } + }, + multiple() { + if(this.funcMode === 'checkbox') { + return true + } else if(this.funcMode === 'radio') { + return false + } else { + return true + } + } + }, + methods: { + _show() { + this.showTree = true + }, + _hide() { + this.showTree = false + }, + _cancel() { + this._hide() + this.$emit("cancel", ''); + }, + _confirm() { + // 处理所选数据 + let rt = this._getCheckedParams(); + this._hide() + + this.$emit("confirm", rt); + }, + _getLabelSlotData(item) { + const _it = this.getItemFromTreeData(this.currentTreeData, item.id); + const it = Object.assign({}, _it); + delete it[this.childrenKey]; + return it + }, + _getCheckedParams() { + // 处理所选数据 + let rt = [], + obj = {}; + this.treeList.forEach((v, i) => { + if (this.treeList[i].checked) { + obj = {} + obj.parents = this.treeList[i].parents + obj = Object.assign(obj, this.treeList[i].source) + // 移除子元素 + // delete obj.children + rt.push(obj) + } + }); + return rt; + }, + _addSubItem(item) { + if(item.disabled) return; + const it = Object.assign({}, item); + if(item.lastRank) { + item.lastRank = false; + item.showChild = true; + } + this.$nextTick(() => { + this.initNum++; + const parentId = item.parentId.concat([]); + const parents = item.parents.concat([]); + parentId.push(item.id); + parents.push(item); + it.disabled = false; + it.rank = it.rank + 1; + it.id = `next-tree-${this.initNum}`; + it.parentId = [...parentId]; + it.parents = [...parents]; + it.show = true; + it.showChild = false; + it.open = false; + it.name = ''; + it.status = 'edit'; + it.source = {}; + it.hideArr = []; + it.lastRank = true; + let index = -1; + for (let i = this.treeList.length - 1; i >= 0; i--) { + if(this.treeList[i].id === item.id) { + index = i; + break; + } + } + if(index !== -1) { + this.treeList.splice(index + 1, 0, it); + } + }) + }, + _addSameItem(item) { + if(item.disabled) return; + const it = Object.assign({}, item); + let index = -1; + for (let i = this.treeList.length - 1; i >= 0; i--) { + if(this.treeList[i].id === item.id || this.treeList[i].parentId.indexOf(item.id) !== -1) { + index = i; + break; + } + } + this.initNum++; + it.id = `next-tree-${this.initNum}`; + it.source = {}; + it.hideArr = []; + it.name = ""; + it.status = 'edit'; + if(index !== -1) { + this.treeList.splice(index + 1, 0, it); + } + }, + async _complete(item) { + if (item.name) { + item.status = 'loading'; + if(item.operateCallback) { + await item.operateCallback('complete', item); + await this.$nextTick(); + } + item.status = ''; + } else { + uni.showToast({ + title: '请先完善内容', + icon: 'none' + }) + } + }, + _editItem(item) { + if(item.disabled) return; + item.status = 'edit'; + }, + _revert() { + this.uiModeInit(); + }, + async _delItem(item) { + if(item.disabled) return; + item.status = 'loading'; + if(item.operateCallback) { + await item.operateCallback('delete', item); + await this.$nextTick(); + } + this.delItemFunc(item); + }, + delItemFunc(item) { + const id = item && item.id ? item.id : ''; + const ids = []; + this.treeList.map((it, index) => { + if(it.id === id) { + ids.push(index); + } else if(it.parentId.indexOf(id) !== -1) { + ids.push(index); + } + }); + ids.sort((a, b) => b - a); + ids.forEach(index => { + this.treeList.splice(index, 1); + }); + }, + checkedFunc(values, state = true) { + if(values instanceof Array) { + values.map(id => { + const item = this.treeList.find(it => it.id === id); + if(item) { + item.checked = !!state + } + }) + } else { + const _item = this.treeList.find(it => it.id === values); + if(_item) { + _item.checked = !!state + } + } + }, + getRenderTreeList(list = [], rank = 0, parentId = [], parents = []) { + const treeList = []; + list.forEach(item => { + const halfChecked = this.getHalfCheckedFormTreeData(item); + let ouputText = ''; + if(this.searchModel === 'depHighlight') { + if(parents && parents.length) { + ouputText = parents.map(item => item[this.labelKey]).join(' > '); + ouputText = ouputText + ' > ' + item[this.labelKey]; + } else { + ouputText = item[this.labelKey]; + } + } + const bool1 = this.expandedKeys.indexOf(item[this.valueKey]) !== -1; + const len = parentId.length; + const bool2 = len > 0 ? this.expandedKeys.indexOf(parentId[len - 1]) !== -1 : bool1; + treeList.push({ + id: item[this.valueKey], + name: item[this.labelKey], + source: item, + parentId, // 父级id数组 + parents, // 父级id数组 + rank, // 层级 + showChild: bool1, //子级是否显示 + open: bool1, //是否打开 + show: (bool1 || bool2) || rank === 0, // 自身是否显示 + hideArr: [], + ouputText, + orChecked: item.checked ? item.checked : false, + checked: item.checked ? item.checked : false, + halfChecked, + disabled: this.disabledKey && item[this.disabledKey] === true, + status: '', + operateCallback: (typeof item.operateCallback) === 'function' ? item.operateCallback : undefined + }) + if(bool1) { + this.nodeInitContrl[item[this.valueKey]] = true; + } else { + this.nodeInitContrl[item[this.valueKey]] = undefined; + } + if ( + (Array.isArray(item[this.childrenKey]) && item[this.childrenKey].length > 0) || + (this.loadData && Array.isArray(item[this.childrenKey]) &&item[this.childrenKey].length === 0) + ) { + let parentid = [...parentId], + parentArr = [...parents], + childrenid = []; + delete parentArr.children + parentid.push(item[this.valueKey]); + parentArr.push({ + [this.valueKey]: item[this.valueKey], + [this.labelKey]: item[this.labelKey], + }) + } else if(item[this.childrenKey] === null) { + treeList[treeList.length - 1].lastRank = false; + } else { + treeList[treeList.length - 1].lastRank = true; + } + }) + return treeList; + }, + //扁平化树结构 + _renderTreeList(list = [], rank = 0, parentId = [], parents = []) { + list.forEach(item => { + const halfChecked = this.getHalfCheckedFormTreeData(item); + let ouputText = ''; + if(this.searchModel === 'depHighlight') { + if(parents && parents.length) { + ouputText = parents.map(item => item[this.labelKey]).join(' > '); + ouputText = ouputText + ' > ' + item[this.labelKey]; + } else { + ouputText = item[this.labelKey]; + } + } + const bool1 = this.expandedKeys.indexOf(item[this.valueKey]) !== -1; + const len = parentId.length; + const bool2 = len > 0 ? this.expandedKeys.indexOf(parentId[len - 1]) !== -1 : bool1; + this.treeList.push({ + id: item[this.valueKey], + name: item[this.labelKey], + source: item, + parentId, // 父级id数组 + parents, // 父级id数组 + rank, // 层级 + showChild: bool1, //子级是否显示 + open: bool1, //是否打开 + show: (bool1 || bool2) || rank === 0, // 自身是否显示 + hideArr: [], + ouputText, + orChecked: item.checked ? item.checked : false, + checked: item.checked ? item.checked : false, + halfChecked, + disabled: this.disabledKey && item[this.disabledKey] === true, + status: '', + operateCallback: (typeof item.operateCallback) === 'function' ? item.operateCallback : undefined + }) + if(bool1) { + this.nodeInitContrl[item[this.valueKey]] = true; + } else { + this.nodeInitContrl[item[this.valueKey]] = undefined; + } + if ( + (Array.isArray(item[this.childrenKey]) && item[this.childrenKey].length > 0) || + (this.loadData && Array.isArray(item[this.childrenKey]) &&item[this.childrenKey].length === 0) + ) { + let parentid = [...parentId], + parentArr = [...parents], + childrenid = []; + delete parentArr.children + parentid.push(item[this.valueKey]); + parentArr.push({ + [this.valueKey]: item[this.valueKey], + [this.labelKey]: item[this.labelKey], + }) + this._renderTreeList(item[this.childrenKey], rank + 1, parentid, parentArr); + } else if(item[this.childrenKey] === null) { + this.treeList[this.treeList.length - 1].lastRank = false; + } else { + this.treeList[this.treeList.length - 1].lastRank = true; + } + }) + }, + // 处理默认选择 + _defaultSelect() { + this.treeList.forEach((v, i) => { + if (v.checked) { + this.treeList.forEach((v2, i2) => { + if (v.parentId.toString().indexOf(v2.parentId.toString()) >= 0) { + v2.show = true + if (v.parentId.includes(v2.id)) { + v2.showChild = true; + v2.open = true; + } + } + }) + } + }) + }, + // 点击 + async _treeItemTap(item, _index) { + const index = this.treeList.findIndex(it =>it.id === item.id); + if (item.lastRank === true) { + if (item.disabled === true) return + if (['checkbox', 'radio'].indexOf(this.funcMode) === -1) return + //点击最后一级时触发事件 + this.treeList[index].checked = !this.treeList[index].checked; + + if(this.changeVerify && (typeof this.changeVerify === 'function')) { + const current = Object.assign({}, item.source); + current.checked = item.checked; + const tip = this.changeVerify(current, this.multiple ? this._getCheckedParams() : [current]); + if(tip) { + this.treeList[index].checked = !this.treeList[index].checked; + uni.showToast({ + title: tip, + icon: 'none' + }); + return + } + }; + this.treeList[index].halfChecked = false; + if(this.multiple && !this.checkStrictly && this.showHalfCheckedTips) { + this.updateHalfChecked(index); + } else if(this.multiple && this.checkStrictly) { + this.updateParentChecked(index); + } + this._fixMultiple(index); + this.$emit("change", this._getCheckedParams()); + return; + } else if(this.ifSearch && this.keywords) { + // 搜索模式下不处理展开收起逻辑 + return + } + // loadData实现 + const isLoadData = this.loadData && !this.nodeInitContrl[item.id]; + if(isLoadData) { + uni && uni.showLoading({ title: "请稍后..." }); + const newChild = await this.loadData({$type: 'nodeLoad', source: this.treeList[index].source}); + // 为了保证treeData数据的完整性,异步加载的数据需要添加到treeData上; + const treeItem = this.getItemFromTreeData(this.currentTreeData, item.id); + treeItem[this.childrenKey] = newChild && newChild.length ? newChild : undefined; + const parentId = item.parentId || []; + const lists = this.getRenderTreeList(newChild || [], item.rank + 1, parentId.concat([item.id]), [{[this.valueKey]: item[this.valueKey],[this.labelKey]: item[this.labelKey]}]); + this.nodeInitContrl[item.id] = true; + this.treeList.splice(index+1, 0, ...lists); + } + const childLen = this.treeList.filter(it => it.parentId.includes(item.id)).length; + if(!isLoadData && childLen > 50) { + uni && uni.showLoading({ title: "请稍后..." }); + } + let list = this.treeList; + let id = item.id; + item.showChild = !item.showChild; + item.open = item.showChild ? true : !item.open; + list.forEach((childItem, i) => { + if (item.showChild === false) { + //隐藏所有子级 + if (!childItem.parentId.includes(id)) { + return; + } else { + if (!this.foldAll) { + if (childItem.lastRank !== true && !childItem.open) { + childItem.showChild = false; + } + // 为隐藏的内容添加一个标记 + if (childItem.show) { + childItem.hideArr[item.rank] = id + } + } else { + if (childItem.lastRank !== true) { + childItem.showChild = false; + } + } + childItem.show = false; + } + } else { + // 打开子集 + if (childItem.parentId[childItem.parentId.length - 1] === id) { + childItem.show = true; + } + + // 打开被隐藏的子集 + if (childItem.parentId.includes(id) && !this.foldAll) { + if (childItem.hideArr[item.rank] === id) { + childItem.show = true; + if (childItem.open && childItem.showChild) { + childItem.showChild = true + } else { + childItem.showChild = false + } + childItem.hideArr[item.rank] = null + } + } else if(this.expandedMode === 'singe' && !childItem.parentId.includes(id)) { + if (childItem.id !== id) { + const bool1 = item.parentId.some(id => { + return (childItem.parentId && childItem.parentId.indexOf(id) !== -1) || childItem.rank === 0; + }); + const childItemParentId = [...childItem.parentId]; + const _id = childItemParentId ? childItemParentId.pop() : ''; + const nodeList = [item, { parentId: childItemParentId.length ? childItemParentId : [] }]; + if(!childItem.lastRank) { + if(item.parentId.indexOf(childItem.id) === -1) { + childItem.showChild = false; + } + + if(!bool1 && !this.isSiblingNode([item, childItem])) { + childItem.show = false; + } else if(childItem.rank !== 0 && _id !== id && this.isSiblingNode(nodeList)) { + childItem.show = false; + } else if (childItem.rank !== 0 && _id !== id && !this.isSiblingNode(nodeList) && this.isParentSiblingNode(nodeList)) { + childItem.show = false; + } + } else { + if(!this.isSiblingNode([item, childItem]) && !bool1) { + childItem.show = false; + } else if(_id !== id && this.isSiblingNode(nodeList)) { + childItem.show = false; + } else if (_id !== id && !this.isSiblingNode(nodeList) && this.isParentSiblingNode(nodeList)) { + childItem.show = false; + } + } + } + } + } + }); + setTimeout(() => { + uni && uni.hideLoading() + }) + }, + isSiblingNode(targetArr = []) { + const target1 = targetArr && targetArr.length ? targetArr[0] : {}; + let target1Id = ''; + if(target1.parentId && target1.parentId.length) { + target1Id = target1.parentId[target1.parentId.length - 1] + } + return targetArr.every(item => { + if(item && item.parentId && item.parentId.length) { + return target1Id === item.parentId[item.parentId.length - 1] + } else { + return target1Id === '' + } + }) + }, + isParentSiblingNode(targetArr = []) { + const target1 = targetArr && targetArr.length ? targetArr[0] : {}; + let target1Id = ''; + if(target1.parentId && target1.parentId.length) { + target1Id = target1.parentId[target1.parentId.length - 1] + } + return targetArr.every(item => { + if(item && item.parentId && item.parentId.length) { + return item.parentId.some(id => id === target1Id) && target1Id !== ''; + } else { + return target1Id === ''; + } + }) + }, + getThemeNodes(text) { + const _text = (text || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const regex = new RegExp(`(${text || ''})`, 'gi'); + return text ? text.replace(regex, `<span style="color: ${this._themeColor}">$1</span>`) : ''; + }, + getNodes(ouputText) { + if(this.keywords && ouputText) { + const key = (this.keywords || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const regex = new RegExp(`(${key})`, 'gi'); + return ouputText.replace(regex, `<span style="color: ${this._themeColor}">$1</span>`); + } + return ouputText + }, + getHalfCheckedFormTreeData(item) { + if (this.checkStrictly) { + return false; + } else if (!this.showHalfCheckedTips) { + return false + } else { + if(item[this.childrenKey] && item[this.childrenKey].length) { + return item[this.childrenKey].some(it => { + if(it.checked === true) { + return true; + } else if(it[this.childrenKey] && it[this.childrenKey].length) { + return this.getHalfCheckedFormTreeData(it); + } else { + return false; + }; + }); + } else { + return false; + } + } + }, + getItemFromTreeData(treeData, id) { + if(id) { + let item = null; + (treeData || []).some(it => { + if(it[this.valueKey] === id) { + item = it + return true + } else if(it[this.childrenKey] && it[this.childrenKey].length) { + item = this.getItemFromTreeData(it[this.childrenKey], id) + return !!item + } else { + return false + } + }); + return item + }; + return null + }, + _treeItemSelect(item, _index) { + const index = this.treeList.findIndex(it =>it.id === item.id); + if (item.disabled === true) return + this.treeList[index].checked = !this.treeList[index].checked; + + if(this.changeVerify && (typeof this.changeVerify === 'function')) { + const current = Object.assign({}, item.source); + current.checked = item.checked; + const tip = this.changeVerify(current, this.multiple ? this._getCheckedParams() : [current]); + if(tip) { + this.treeList[index].checked = !this.treeList[index].checked; + uni.showToast({ + title: tip, + icon: 'none' + }); + return + } + }; + this.treeList[index].halfChecked = false; + if (this.multiple && this.checkStrictly) { + if(!item.lastRank) { + const source = item.source || {}; + const children = source[this.childrenKey] || []; + const checkedKeyList = this.getChildrenKeys(children); + this.treeList.forEach((v, i) => { + if(checkedKeyList.indexOf(v.id) !== -1) { + if(this.checkStrictlyModel === 'weak') { + if(!this.treeList[i].disabled) { + this.treeList[i].checked = this.treeList[index].checked + } + } else if(this.checkStrictlyModel === 'strong') { + this.treeList[i].checked = this.treeList[index].checked + } + } + }) + } + this.updateParentChecked(index) + } else if(this.multiple && !this.checkStrictly && this.showHalfCheckedTips) { + this.updateHalfChecked(index); + } else { + this._fixMultiple(index); + } + + this.$emit("change", this._getCheckedParams()); + }, + updateParentChecked(index) { + const parentId = (this.treeList[index].parentId || []).concat([]).reverse(); + if(parentId && parentId.length) { + parentId.map(id => { + const parentTreeDataItem = this.getItemFromTreeData(this.currentTreeData, id); + const childrenIds = (parentTreeDataItem[this.childrenKey] || []).map(item => item[this.valueKey]); + const bool = this.treeList + .filter(it => childrenIds.indexOf(it.id) !== -1) + .every(it => it.checked === true); + + const _bool = this.treeList + .filter(it => childrenIds.indexOf(it.id) !== -1) + .every(it => it.checked === false); + + const parentItem = this.treeList.find(it => it.id === id); + if(parentItem) { + if (this.checkStrictlyModel === 'weak') { + if(bool && !parentItem.disabled) { + parentItem.checked = true; + } else if(_bool && !parentItem.disabled) { + parentItem.checked = false; + } + } else if(this.checkStrictlyModel === 'strong') { + if(bool) { + parentItem.checked = true; + } else { + parentItem.checked = false; + } + } + } + }) + } + }, + updateHalfChecked(index) { + const _parentId = this.treeList[index].parentId || []; + const parentId = _parentId.concat([]).reverse(); + if(parentId && parentId.length) { + parentId.map(id => { + const parentTreeDataItem = this.getItemFromTreeData(this.currentTreeData, id); + const childrenIds = (parentTreeDataItem[this.childrenKey] || []).map(item => item[this.valueKey]); + + const bool = this.treeList + .filter(it => childrenIds.indexOf(it.id) !== -1) + .every(it => it.checked === false && it.halfChecked === false); + + const _bool = this.treeList + .filter(it => childrenIds.indexOf(it.id) !== -1) + .some(it => it.checked === true || it.halfChecked === true); + + const parentItem = this.treeList.find(it => it.id === id); + if(parentItem) { + if(!parentItem.checked) { + if(bool) { + parentItem.halfChecked = false + } else if (_bool) { + parentItem.halfChecked = true + } else { + parentItem.halfChecked = false + } + } + } + }) + } + if(this.treeList[index].checked == false) { + const source = this.treeList[index].source || {}; + const children = source[this.childrenKey] || []; + const checkedKeyList = this.getChildrenKeys(children); + const bool = this.treeList.filter(item => checkedKeyList.indexOf(item.id) !== -1).some(item => item.checked); + if(bool) { + this.treeList[index].halfChecked = true; + } + } + }, + showHalfChecked(item) { + if(this.multiple && !this.checkStrictly && item.halfChecked === true) { + return true + } else { + return false + } + }, + getChildrenKeys(children) { + let keys = []; + (children || []).map(item => { + keys.push(item[this.valueKey]) + if (item[this.childrenKey] && item[this.childrenKey].length) { + keys = keys.concat(this.getChildrenKeys(item[this.childrenKey])) + } + }) + return keys + }, + // 处理单选多选 + _fixMultiple(index) { + if (!this.multiple) { + // 如果是单选 + this.treeList.forEach((v, i) => { + if (i != index) { + this.treeList[i].checked = false + } else { + this.treeList[i].checked = true + } + }) + } + }, + // 重置数据 + _reTreeList() { + this.treeList.forEach((v, i) => { + this.treeList[i].checked = v.orChecked + }) + }, + _initTree(){ + this.treeList.length = 0; + if(this.loadData) { + this.currentTreeData = JSON.parse(JSON.stringify(this.treeData)); + } else { + this.currentTreeData = this.treeData; + } + this._renderTreeList(this.currentTreeData); + this.$nextTick(() => { + this._defaultSelect(); + }) + }, + _clear() { + this.treeList.map(item => { + if(this.multiple && this.checkStrictly) { + if(this.checkStrictlyModel === 'strong') { + item.checked = false; + } else if(this.checkStrictlyModel === 'weak') { + if(!item.disabled) { + item.checked = false; + }; + } else { + item.checked = false; + } + } else { + if(!item.disabled) { + item.checked = false; + } + }; + + item.halfChecked = false; + }) + this.$emit("change", this._getCheckedParams()); + this.$emit("clear"); + }, + async onSearch(val) { + if(this.searchModel === 'remote') { + if(this.loadData) { + if(val) { + const newChild = await this.loadData({$type: 'remoteSearch', source: val}); + const lists = this.getRenderTreeList(newChild); + this.treeList = [...lists]; + } else { + this._initTree(); + } + } else { + uni.showToast({ + title: "使用远程搜索模式,需要配置loadData函数;", + icon: 'none' + }) + } + } else { + this.keywords = val; + } + }, + initUiModePopup(watched) { + uni.getSystemInfo({ + success: (res) => { + this.top = (res.windowHeight - this.height) + 'px'; + } + }); + watched && watched(); + watched = this.$watch(() => this.showTree, (bool) => { + if(bool) { + this._initTree(); + } else { + this.$nextTick(() => { + setTimeout(() => { + this.treeList.length = 0; + this.nodeInitContrl = {}; + }) + }) + } + }, {immediate: true}); + }, + initUiModePage(watched) { + this.top = '0px'; + watched && watched(); + watched = this.$watch(() => this.treeData, () => { + this.treeList.length = 0; + this.nodeInitContrl = {}; + this.$nextTick(() => { + this._initTree(); + }); + }, {immediate: true, deep: true}) + this.$nextTick(() => { + this.showTree = true; + }); + }, + uiModeInit() { + let watched = null; + if(this.uiMode === 'popup') { + this.initUiModePopup(watched); + } else if (this.uiMode === 'page') { + this.initUiModePage(watched); + } else { + this.initUiModePopup(watched); + } + } + }, + mounted() { + console.log('----------next-tree组件完成挂载demo------------') + this.uiModeInit(); + } +} +</script> + +<style scoped> +@import "./style.css"; +</style> diff --git a/uni_modules/next-tree/components/next-tree/style.css b/uni_modules/next-tree/components/next-tree/style.css new file mode 100644 index 0000000..3ae61fc --- /dev/null +++ b/uni_modules/next-tree/components/next-tree/style.css @@ -0,0 +1,272 @@ +@font-face { + font-family: 'iconfont'; + src: url('//at.alicdn.com/t/c/font_4110624_qs48wckazsh.ttf?t=1712479573821') format('truetype'); +} +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.iconfont { + font-family: iconfont; + font-style: normal; +} +.iconfont-loading { + font-family: iconfont; + display: inline-block; + font-style: normal; + animation: spin 1s linear infinite; +} +.next-tree-mask { + position: fixed; + top: 0rpx; + right: 0rpx; + bottom: 0rpx; + left: 0rpx; + z-index: 997; + background-color: rgba(0, 0, 0, 0.6); + opacity: 0; + transition: all 0.3s ease; + visibility: hidden; +} +.next-tree-mask.show { + visibility: visible; + opacity: 1; +} +.next-tree-cnt { + position: fixed; + top: 0rpx; + right: 0rpx; + bottom: 0rpx; + left: 0rpx; + z-index: 997; + top: 360rpx; + transition: all 0.3s ease; + transform: translateY(100%); +} +.next-tree-cnt.next-tree-cnt-page { + transition: none; +} +.next-tree-cnt.show { + transform: translateY(0); +} +.next-tree-bar { + background-color: #fff; + height: 72rpx; + padding-left: 20rpx; + padding-right: 20rpx; + display: flex; + justify-content: space-between; + align-items: center; + box-sizing: border-box; + border-bottom-width: 1rpx !important; + border-bottom-style: solid; + border-bottom-color: #f5f5f5; + font-size: 32rpx; + color: #757575; + line-height: 1; +} +.next-tree-bar-btns { + display: inline-block; + display: flex; + flex-direction: row; +} +.btn-divid { + display: inline-block; + width: 1px; + margin: 0 10px; + background-color: #ccc; +} +.next-tree-bar-confirm { + color: #f9ae3d; +} +.next-tree-view { + position: absolute; + top: 0rpx; + right: 0rpx; + bottom: 0rpx; + left: 0rpx; + top: 72rpx; + background-color: #fff; + padding-top: 20rpx; + padding-right: 20rpx; + padding-bottom: 20rpx; + padding-left: 20rpx; +} +.next-tree-view-sc { + height: 100%; + overflow: hidden; +} +.next-tree-view-sc .empty { + text-align: center; + color: #757575; + padding: 30rpx; +} +.next-tree-item-block { + +} +.next-tree-item { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 26rpx; + color: #757575; + line-height: 1; + height: 0; + opacity: 0; + transition: 0.2s; + position: relative; + overflow: hidden; +} +.next-tree-item .icon-btn { + font-size: 30rpx; + margin-right: 20rpx; +} +.next-tree-item .left-line { + position: relative; + width: 1rpx; + height: 100%; + box-sizing: border-box; +} +.next-tree-item .left-line::before { + position: absolute; + content: ""; + width: 1rpx; + height: 100%; + background-color: rgba(204,204,204,0.9); + box-sizing: border-box; + + left: -18rpx; +} +.next-tree-item .parent-horizontal-line { + width: 1rpx; + height: 100%; + position: absolute; + top: 0; + left: 0rpx; + box-sizing: border-box; + background-color: rgba(204,204,204,0.9); +} +.next-tree-item .left-line .horizontal-line { + width: 20rpx; + height: 1rpx; + position: absolute; + top: 40rpx; + left: 0rpx; + background-color: rgba(204,204,204,0.9); + box-sizing: border-box; +} + +.next-tree-item.show { + min-height: 80rpx; + margin-top: 10rpx; + height: 100%; + opacity: 1; +} +.next-tree-item.showchild:before { + transform: rotate(90deg); +} +.next-tree-item.border { + border-bottom: 1rpx solid rgba(204,204,204,0.2); +} +.next-tree-item.last:before { + opacity: 0; +} +.next-tree-item.disabled { + color: #ccc!important; +} + +.next-tree-icon { + width: 26rpx; + height: 26rpx; + margin-right: 8rpx; +} +.next-tree-label { + flex: 1; + display: flex; + align-items: center; + height: 100%; + line-height: 1.2; +} +.next-tree-label .label-input { + border: 1rpx solid #f0f0f0; + border-radius: 10rpx; + width: 100%; + padding: 12rpx 18rpx; + margin-right: 30rpx; +} +.next-tree-check { + width: 40px; + height: 40px; + display: flex; + justify-content: center; + align-items: center; +} +.next-tree-check-yes, +.next-tree-check-no { + width: 20px; + height: 20px; + border-top-left-radius: 20%; + border-top-right-radius: 20%; + border-bottom-right-radius: 20%; + border-bottom-left-radius: 20%; + border-top-width: 1rpx; + border-left-width: 1rpx; + border-bottom-width: 1rpx; + border-right-width: 1rpx; + border-style: solid; + border-color: #f9ae3d; + display: flex; + justify-content: center; + align-items: center; + box-sizing: border-box; +} +.next-tree-check-yes-b { + border-top-left-radius: 20%; + border-top-right-radius: 20%; + border-bottom-right-radius: 20%; + border-bottom-left-radius: 20%; + background-color: #f9ae3d; + color: #fff; +} +.next-tree-check-yes-b .icon-text { + font-size: 14px; + font-weight: normal; + font-family: uicon-iconfont; + display: flex; + flex-direction: row; + align-items: center; +} +.next-tree-check .radio { + border-top-left-radius: 50%; + border-top-right-radius: 50%; + border-bottom-right-radius: 50%; + border-bottom-left-radius: 50%; +} +.next-tree-check .radio .next-tree-check-yes-b { + border-top-left-radius: 50%; + border-top-right-radius: 50%; + border-bottom-right-radius: 50%; + border-bottom-left-radius: 50%; +} + +.next-tree-item.disabled .next-tree-check-no { + color: #ccc!important; +} +.next-tree-item.disabled .next-tree-check-yes-b { + background-color: #ccc!important; +} +.hover-c { + opacity: 0.6; +} + +.fixed-bottom-bar { + border-top: 1px solid #ccc; + position: fixed; + bottom: 0rpx; + left: 0rpx; + right: 0rpx; + z-index: 998; +} + + diff --git a/uni_modules/next-tree/package.json b/uni_modules/next-tree/package.json new file mode 100644 index 0000000..3a0280a --- /dev/null +++ b/uni_modules/next-tree/package.json @@ -0,0 +1,84 @@ +{ + "id": "next-tree", + "displayName": "next-tree(超强树选择器、树组件、树插件、无限级联树、单选树、多选树、自定义样式树、树形选择器)", + "version": "1.8.9", + "description": "next-tree 弹窗树形选择器,支持多选,支持大数据, 无限级联,单选,父子级级联,远程/ajax加载,子节点增量/异步渲染,自定义样式定制,具名插槽等;支持h5/小程序/APP,全端通用", + "keywords": [ + "树选择", + "tree", + "弹窗树选择器", + "多选树", + "单选树" +], +"engines": { + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "修改版本说明", + "permissions": "无" + }, + "npmurl": "", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["next-search-more"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "y" + }, + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "n", + "Edge": "y", + "Firefox": "y", + "Safari": "u" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "u", + "QQ": "y", + "钉钉": "y", + "快手": "u", + "飞书": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/next-tree/readme.md b/uni_modules/next-tree/readme.md new file mode 100644 index 0000000..3d6d61d --- /dev/null +++ b/uni_modules/next-tree/readme.md @@ -0,0 +1,843 @@ + +## next-tree --下拉树 + +> 遇到问题或有建议可以加入QQ群(<font color=#f00>455948571</font>)反馈 +> 如果觉得组件不错,<font color=#f00>给五星鼓励鼓励</font>咯! + +## 亮点功能说明(打造你不得不用的好插件) + +### 本插件自1.5.0版本后支持一下功能 + + > 1.大数据量渲染(本插件智能判断,如果子孙集数据量大于50时,会响应等待渲染视图;) + > 2.子节点按需渲染(自动启用,无需配置) + > 3.父子级联选择设置 + > 4.单选多选设置 + > 5.父节点是否可选设置 + > 6.回显默认选中值 + > 7.不可选项disabled设置 + > 8.增强大数据量体验交互,增加筛选搜索模式 + > 9.增强样式定制,提供自定义插槽,实现高要求样式定制 + > 10.增加辅助线模式,外观更加精美 + > 11.支持动态配置title + > 12.支持搜索模式searchModel=depHighlight模式,从属高亮显示模式 + > 13.支持异步加载子节点,ajax加载子节点 + > 14.增加可配置主题,自由定制插件主题颜色 + > 15.支持动态校验,可以进行提示控制校验 + > 16.支持页面模式/弹层模式,可以进行单页面展示或者弹层展示 + > 17.支持半选提示状态显示 + > 18.支持展开项expandedKeys配置 + > 19.全面支持vue2/vue3 + > 20.增加无子节点的父节点配置支持(当item[childrenKey]为null时,代表无子节点的父节点) + > 21.终极支持超数据量使用,增加展开模式配置单链路配置使用expandedMode=singe,使得ui组件使用进一步不在限制与数据量 + > 22.功能模式再次增强,支持单选tree,多选tree,编辑tree,展示tree; + +## 注意 + +### 作者不介意你对组件源码进行改造使用,为了开源更加高效,谢谢你的配合;为了节省不必要的沟通浪费,以下情况请不要再反馈给作者,请自行解决; +### 在这感各位的理解,我支持开源,但是作者时间有限;谢谢各位的配合;在这里期望我写的小小插件能为你提供便捷; + + > 1.如果你对源码进行了修改使用,请不需要对作者做任何的反馈,作者确实没有空陪你做技术分析解答; + > 2.如果你引入插件,连插件是否有正常被uniapp框架识别解析都不清楚,请你换个插件使用; + > 3.如果你引入插件,针对自己项目进行功能改造的,请自行仔细阅读源码并了解其原理,自行改造;这里作者不愿意浪费过多时间进行技术解答; + > 4.如果你不想进行全局加载next-tree,需要按需加载;next-tree中有相关依赖的组件,需要你自行在组件内部单独引入;依赖组件可以在package.json中找到; + > 5.理论上作者不再解决由于本地开发环境问题所导致的插件使用问题,请自行到uniapp官网学习解决; + +## 使用 +### 超集功能即将不对外开源; + +>[从uniapp插件市场导入](https://ext.dcloud.net.cn/plugin?name=next-tree) + +## 关注作者的动态 +[点击进入主页,关注作者](https://ask.dcloud.net.cn/people/ponder_7464) + +## 关注作者其他开源 + +npm开源包:[npm](https://www.npmjs.com/~lixueshiaa); +github开源项目:[github](https://github.com/lixueshiaa); + + +```html +<template> + <view style="padding:10px;color: #333;font-weight: 500;"> + <view style="padding: 10px 0"><text>1、设置单选和父级不可选</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="itemclick(false, false)">设置</button> + <view style="padding: 10px 0"><text>2、设置多选和父级不可选</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="itemclick(true, false)">设置</button> + <view style="padding: 10px 0"><text>3、设置单选和父级可选</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="itemclick(false, true)">设置</button> + <view style="padding: 10px 0"><text>4、设置多选和父级可选</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="itemclick(true, true)" >设置</button> + <view style="padding: 10px 0"><text>4、设置多选和父级可选和父级关联子级选择</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="itemclick(true, true, true)" >设置</button> + <view style="padding: 10px 0"><text>5、设置默认回显(默认选中: '上海-2', '黄埔区-35')</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="echoDefault()" >设置</button> + <!-- 异步加载demo --> + <view style="padding: 10px 0"><text>6、异步加载渲染demo</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="openTree()" >设置</button> + </view> + + <!-- 异步加载demo --> + <next-tree :selectParent="false" :checkStrictly="true" funcMode="checkbox" ref="nextTreeAsyncRef" :treeData="asyncTreeData" :loadData="loadData" /> + + <next-tree :expandedKeys="['3','3-1']" :changeVerify="changeVerify" :title="getTitle" ref="nextTreeRef" :checkStrictly="checkStrictly" :selectParent="selectParent" :funcMode="funcMode" :treeData="treeData" @cancel="oncancel" @confirm="onconfirm"> + <!-- label插槽示意代码 --> + <!-- <template #label="{data: {id, label, iconSrc, prev, post}}"> + <view class="line-block"> + <image class="img" v-if="iconSrc" :src="iconSrc"></image> + <text space="nbsp" v-if="prev">{{prev}} </text><text>{{label}}</text><text space="nbsp" v-if="post"> {{post}}</text> + </view> + </template> --> + <!-- <template #topBar> + <view style="color: #666;padding:5px;"><text style="font-size: 12px;">历史记录</text></view> + <view style="display: flex;justify-content: space-between;padding-bottom: 10px;border-bottom: 1rpx solid #f0f0f0;"> + <button @click="checkedFunc('1-3-3-4')" :style="'background-color:'+ (activeId === '1-3-3-4' ? '#f9ae3d' : '#ccc') + ';color:#fff;margin: 5px'" size="mini">北京区-4</button> + <button @click="checkedFunc('3-1-2')" :style="'background-color:'+ (activeId === '3-1-2' ? '#f9ae3d' : '#ccc') + ';color:#fff;margin: 5px'" size="mini">海珠区-2</button> + <button @click="checkedFunc('3-1-6')" :style="'background-color:'+ (activeId === '3-1-6' ? '#f9ae3d' : '#ccc') + ';color:#fff;margin: 5px'" size="mini">海珠区-5</button> + </view> + </template> --> + </next-tree> +</template> +``` + +### vue3 + ts 使用 + +```ts +<script setup lang="ts"> +import { ref, unref } from 'vue' +import nextTree from '@/components/next-tree/next-tree.vue' + +const funcMode = ref('radio'); +const selectParent = ref(false) +const nextTreeRef = ref() +const nextTreeAsyncRef = ref() +const activeId = ref('') + +const localData: any = { + 'a1': [{id: 'a1-1', label: 'a1-1'}, {id: 'a1-2', label: 'a1-2',children: [] },{id: 'a1-3', label: 'a1-3'}], + 'b1': [{id: 'b1-1', label: 'b1-1',children: []}, {id: 'b1-2', label: 'b1-2'},{id: 'b1-3', label: 'b1-3'}], + 'c1': [{id: 'c1-1', label: 'c1-1'}, {id: 'c1-2', label: 'c1-2'},{id: 'c1-3', label: 'c1-3',children: []}], + 'a1-2': [{id: 'a1-2-1', label: 'a1-2-1'}, {id: 'a1-2-2', label: 'a1-2-2'}], + 'b1-1': [{id: 'b1-1-1', label: 'b1-1-1'}, {id: 'b1-1-2', label: 'b1-1-2'}], + 'c1-3': [{id: 'c1-3-1', label: 'c1-3-1'}, {id: 'c1-3-2', label: 'c1-3-2'}] +} + +const checkStrictly = ref(false) +const asyncTreeData = ref([{id: 'a1', label: 'a1', children: []},{id: 'b1', label: 'b1', children: []},{id: 'c1', label: 'c1', children: []}]) +const treeData = ref([ + {id: '1', label: '北京'}, + {id: '2', label: '上海', children: [ + {id: '2-1', label: '上海-1'}, + {id: '2-2', label: '上海-2'}, + {id: '2-3', label: '上海-3'}, + ] }, + {id: '3', label: '广州', children: [ + {id: '3-1', label: '海珠区', children: [ + {id: '3-1-1', label: '海珠区-1'}, + {id: '3-1-2', label: '海珠区-2'}, + {id: '3-1-4', label: '海珠区-3'}, + {id: '3-1-5', label: '海珠区-4'}, + {id: '3-1-6', label: '海珠区-5'}, + {id: '3-1-7', label: '海珠区-6'}, + {id: '3-1-8', label: '海珠区-7'}, + {id: '3-1-9', label: '海珠区-8'}, + {id: '3-1-10', label: '海珠区-9'}, + {id: '3-1-11', label: '海珠区-10'}, + ]}, + {id: '3-2', label: '番禺区', children: [ + {id: '3-2-1', label: '番禺区-1'}, + {id: '3-2-2', label: '番禺区-2'}, + {id: '3-2-4', label: '番禺区-3', children: null}, // 注意: 当childrenKey的值设为null,代表无子节点的父节点 + {id: '3-2-5', label: '番禺区-4'}, + {id: '3-2-6', label: '番禺区-5'}, + {id: '3-2-7', label: '番禺区-6'}, + {id: '3-2-8', label: '番禺区-7'}, + {id: '3-2-9', label: '番禺区-8'}, + {id: '3-2-10', label: '番禺区-9'}, + {id: '3-2-11', label: '番禺区-10'}, + ]}, + {id: '3-3', label: '黄埔区', children: [ + {id: '3-3-1', label: '黄埔区-1'}, + {id: '3-3-2', label: '黄埔区-2'}, + {id: '3-3-3', label: '黄埔区-3'}, + {id: '3-3-4', label: '黄埔区-4'}, + {id: '3-3-5', label: '黄埔区-5'}, + {id: '3-3-6', label: '黄埔区-6'}, + {id: '3-3-7', label: '黄埔区-7'}, + {id: '3-3-8', label: '黄埔区-8'}, + {id: '3-3-9', label: '黄埔区-9'}, + {id: '3-3-10', label: '黄埔区-10'}, + {id: '3-3-12', label: '黄埔区-11'}, + {id: '3-3-13', label: '黄埔区-12'}, + {id: '3-3-13', label: '黄埔区-13'}, + {id: '3-3-14', label: '黄埔区-14'}, + {id: '3-3-15', label: '黄埔区-15'}, + {id: '3-3-16', label: '黄埔区-16'}, + {id: '3-3-17', label: '黄埔区-17'}, + {id: '3-3-18', label: '黄埔区-18'}, + {id: '3-3-19', label: '黄埔区-19'}, + {id: '3-3-20', label: '黄埔区-20'}, + {id: '3-3-21', label: '黄埔区-21'}, + {id: '3-3-22', label: '黄埔区-22'}, + {id: '3-3-23', label: '黄埔区-23'}, + {id: '3-3-24', label: '黄埔区-24'}, + {id: '3-3-25', label: '黄埔区-25'}, + {id: '3-3-26', label: '黄埔区-26'}, + {id: '3-3-27', label: '黄埔区-27'}, + {id: '3-3-28', label: '黄埔区-28'}, + {id: '3-3-29', label: '黄埔区-29'}, + {id: '3-3-30', label: '黄埔区-30'}, + {id: '3-3-31', label: '黄埔区-31'}, + {id: '3-3-32', label: '黄埔区-32'}, + {id: '3-3-33', label: '黄埔区-33'}, + {id: '3-3-34', label: '黄埔区-34'}, + {id: '3-3-35', label: '黄埔区-35'}, + {id: '3-3-36', label: '黄埔区-36'}, + + ]}, + ], + }]) +function getTitle(checked) { + return `已选:${checked.length}项` +} +function itemclick (_multiple, _selectParent, _checkStrictly = false) { + funcMode.value = _multiple ? 'checkbox' : 'radio'; + selectParent.value = _selectParent + checkStrictly.value = _checkStrictly + unref(nextTreeRef).showTree = true +} +function checkedFunc(id) { + if(unref(activeId) === id) { + activeId.value = ''; + unref(nextTreeRef).checkedFunc(id, false) + } else { + activeId.value = id; + unref(nextTreeRef).checkedFunc(id) + } +} +function changeVerify(current, chooseList) { + // 注意:返回非空字符串会阻止原有行为,并提示返回的字符串 + // 如果函数体不做return返回值,即验证通过,控件正常处理业务 + console.log('当前变化的数据', current) + console.log('已选择的数据', chooseList) + if(chooseList && chooseList.length > 4) { + + return '最多可以选择4个节点' + } +} +function openTree() { + unref(nextTreeAsyncRef).showTree = true +} +function echoDefault () { + const selectIds = ['2-1','3-3-35'] + checkedTreeData(unref(treeData), selectIds) + console.log('treeData的数据:', unref(treeData)) + funcMode.value = 'checkbox' + unref(nextTreeRef).showTree = true +} +function loadData(data) { + const type = data.$type; // 加载类型 + const source = data.source // 源数据 + // 同步实现的代码处理方式 + + if (type === 'nodeLoad') { + const nodeItem = source; + // 同步实现的代码处理方式 + // 如果期望子集节点中还存在孙子节点可以打开,请在初始化数据的时候,初始化个空数组的子节点配置值{[this.childrenKey]: []} + + // if(nodeItem && localData[nodeItem.id]) { + // return localData[nodeItem.id] + // } else { + // return [] + // } + + // 异步的代码实现方式 + // 如果期望子集节点中还存在孙子节点可以打开,请在初始化数据的时候,初始化个空数组的子节点配置值{[this.childrenKey]: []} + + return new Promise((resolve, reject) => { + setTimeout(() => { + if(nodeItem && localData[nodeItem.id]) { + return resolve(localData[nodeItem.id]) + } else { + return resolve([]) + } + }, 1000) + }) + } else if(type === 'remoteSearch') { // searchModel=remote的时候,会在loadData函数中返回type参数供做业务处理 + // ...doing + } + +} + +function checkedTreeData (treeData, selectIds) { + treeData.map(item => { + if (selectIds.indexOf(item.id) !== -1) { + item.checked = true + } else { + item.checked = false + } + if (item.children && item.children.length) { + checkedTreeData(item.children, selectIds) + } + }) +} +function oncancel() { + // 清除treeData的选中状态 + checkedTreeData(unref(treeData), []) +} +function onconfirm(list) { + console.log('选中项的数量列表list:', list) +} +</script> +<style lang="scss"> + .line-block { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + .img { + width: 40rpx; + height: 40rpx; + border-radius: 10rpx; + margin: 0 20rpx; + } + } +</style> + +``` + +### vue2 使用 +```html +<template> + <view> + <view style="padding:10px;color: #333;font-weight: 500;"> + <view style="padding: 10px 0"><text>1、设置单选和父级不可选</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="itemclick(false, false)">设置</button> + <view style="padding: 10px 0"><text>2、设置多选和父级不可选</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="itemclick(true, false)">设置</button> + <view style="padding: 10px 0"><text>3、设置单选和父级可选</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="itemclick(false, true)">设置</button> + <view style="padding: 10px 0"><text>4、设置多选和父级可选</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="itemclick(true, true)" >设置</button> + <view style="padding: 10px 0"><text>4、设置多选和父级可选和父级关联子级选择</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="itemclick(true, true, true)" >设置</button> + <view style="padding: 10px 0"><text>5、设置默认回显(默认选中: '上海-2', '黄埔区-35')</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="echoDefault()" >设置</button> + <!-- 异步加载demo --> + <view style="padding: 10px 0"><text>6、异步加载渲染demo</text></view> + <button style="width: 100%;background-color: #f9ae3d;color:#fff" size="mini" @click="openTree()" >设置</button> + </view> + <!-- 异步加载demo --> + <next-tree :selectParent="false" :checkStrictly="true" funcMode="checkbox" ref="nextTreeAsyncRef" :treeData="asyncTreeData" :loadData="loadData" /> + + <next-tree :expandedKeys="['3','3-1']" :changeVerify="changeVerify" :title="getTitle" ref="nextTreeRef" :checkStrictly="checkStrictly" :selectParent="selectParent" :funcMode="funcMode" :treeData="treeData" @cancel="oncancel" @confirm="onconfirm"> + <!-- label插槽示意代码 --> + <!-- <template v-slot:label="{data}"> + <view class="line-block"> + <image class="img" v-if="data.iconSrc" :src="data.iconSrc"></image> + <text space="nbsp" v-if="data.prev">{{data.prev}} </text><text>{{data.label}}</text><text space="nbsp" v-if="data.post"> {{data.post}}</text> + </view> + </template> --> + <!-- <template #topBar> + <view style="color: #666;padding:5px;"><text style="font-size: 12px;">历史记录</text></view> + <view style="display: flex;justify-content: space-between;padding-bottom: 10px;border-bottom: 1rpx solid #f0f0f0;"> + <button @click="checkedFunc('1-3-3-4')" :style="'background-color:'+ (activeId === '1-3-3-4' ? '#f9ae3d' : '#ccc') + ';color:#fff;margin: 5px'" size="mini">北京区-4</button> + <button @click="checkedFunc('3-1-2')" :style="'background-color:'+ (activeId === '3-1-2' ? '#f9ae3d' : '#ccc') + ';color:#fff;margin: 5px'" size="mini">海珠区-2</button> + <button @click="checkedFunc('3-1-6')" :style="'background-color:'+ (activeId === '3-1-6' ? '#f9ae3d' : '#ccc') + ';color:#fff;margin: 5px'" size="mini">海珠区-5</button> + </view> + </template> --> + </next-tree> + </view> +</template> +``` + +```js +<script> +let self = null; +export default { + data () { + return { + funcMode: 'radio', + selectParent: false, + checkStrictly: false, + activeId: '', + localData:{ + 'a1': [{id: 'a1-1', label: 'a1-1'}, {id: 'a1-2', label: 'a1-2',children: [] },{id: 'a1-3', label: 'a1-3'}], + 'b1': [{id: 'b1-1', label: 'b1-1',children: []}, {id: 'b1-2', label: 'b1-2'},{id: 'b1-3', label: 'b1-3'}], + 'c1': [{id: 'c1-1', label: 'c1-1'}, {id: 'c1-2', label: 'c1-2'},{id: 'c1-3', label: 'c1-3',children: []}], + 'a1-2': [{id: 'a1-2-1', label: 'a1-2-1'}, {id: 'a1-2-2', label: 'a1-2-2'}], + 'b1-1': [{id: 'b1-1-1', label: 'b1-1-1'}, {id: 'b1-1-2', label: 'b1-1-2'}], + 'c1-3': [{id: 'c1-3-1', label: 'c1-3-1'}, {id: 'c1-3-2', label: 'c1-3-2'}] + }, + asyncTreeData: [{id: 'a1', label: 'a1', children: []},{id: 'b1', label: 'b1', children: []},{id: 'c1', label: 'c1', children: []}], + treeData: [ + {id: '1', label: '北京', checked: false}, + {id: '2', label: '上海', checked: false, children: [ + {id: '2-1', label: '上海-1', checked: false}, + {id: '2-2', label: '上海-2', checked: false}, + {id: '2-3', label: '上海-3', checked: false}, + ] }, + {id: '3', label: '广州', children: [ + {id: '3-1', label: '海珠区', checked: false, children: [ + {id: '3-1-1', label: '海珠区-1', checked: false, disabled: true}, + {id: '3-1-2', label: '海珠区-2', checked: false}, + {id: '3-1-4', label: '海珠区-3', checked: false}, + {id: '3-1-5', label: '海珠区-4', checked: false}, + {id: '3-1-6', label: '海珠区-5', checked: false}, + {id: '3-1-7', label: '海珠区-6', checked: false}, + {id: '3-1-8', label: '海珠区-7', checked: false}, + {id: '3-1-9', label: '海珠区-8', checked: false}, + {id: '3-1-10', label: '海珠区-9', checked: false}, + {id: '3-1-11', label: '海珠区-10', checked: false}, + ]}, + {id: '3-2', label: '番禺区', checked: false, children: [ + {id: '3-2-1', label: '番禺区-1', checked: false}, + {id: '3-2-2', label: '番禺区-2', checked: false}, + {id: '3-2-4', label: '番禺区-3', checked: false}, + {id: '3-2-5', label: '番禺区-4', checked: false}, + {id: '3-2-6', label: '番禺区-5', checked: false}, + {id: '3-2-7', label: '番禺区-6', checked: false}, + {id: '3-2-8', label: '番禺区-7', checked: false}, + {id: '3-2-9', label: '番禺区-8', checked: false}, + {id: '3-2-10', label: '番禺区-9', checked: false}, + {id: '3-2-11', label: '番禺区-10', checked: false}, + ]}, + {id: '3-3', label: '黄埔区', checked: false, children: [ + {id: '3-3-1', label: '黄埔区-1', checked: false}, + {id: '3-3-2', label: '黄埔区-2', checked: false}, + {id: '3-3-3', label: '黄埔区-3', checked: false}, + {id: '3-3-4', label: '黄埔区-4', checked: false}, + {id: '3-3-5', label: '黄埔区-5', checked: false}, + {id: '3-3-6', label: '黄埔区-6', checked: false}, + {id: '3-3-7', label: '黄埔区-7', checked: false}, + {id: '3-3-8', label: '黄埔区-8', checked: false}, + {id: '3-3-9', label: '黄埔区-9', checked: false}, + {id: '3-3-10', label: '黄埔区-10', checked: false}, + {id: '3-3-12', label: '黄埔区-11', checked: false}, + {id: '3-3-13', label: '黄埔区-12', checked: false}, + {id: '3-3-13', label: '黄埔区-13', checked: false}, + {id: '3-3-14', label: '黄埔区-14', checked: false}, + {id: '3-3-15', label: '黄埔区-15', checked: false}, + {id: '3-3-16', label: '黄埔区-16', checked: false}, + {id: '3-3-17', label: '黄埔区-17', checked: false}, + {id: '3-3-18', label: '黄埔区-18', checked: false}, + {id: '3-3-19', label: '黄埔区-19', checked: false}, + {id: '3-3-20', label: '黄埔区-20', checked: false}, + {id: '3-3-21', label: '黄埔区-21', checked: false}, + {id: '3-3-22', label: '黄埔区-22', checked: false}, + {id: '3-3-23', label: '黄埔区-23', checked: false}, + {id: '3-3-24', label: '黄埔区-24', checked: false}, + {id: '3-3-25', label: '黄埔区-25', checked: false}, + {id: '3-3-26', label: '黄埔区-26', checked: false}, + {id: '3-3-27', label: '黄埔区-27', checked: false}, + {id: '3-3-28', label: '黄埔区-28', checked: false}, + {id: '3-3-29', label: '黄埔区-29', checked: false}, + {id: '3-3-30', label: '黄埔区-30', checked: false}, + {id: '3-3-31', label: '黄埔区-31', checked: false}, + {id: '3-3-32', label: '黄埔区-32', checked: false}, + {id: '3-3-33', label: '黄埔区-33', checked: false}, + {id: '3-3-34', label: '黄埔区-34', checked: false}, + {id: '3-3-35', label: '黄埔区-35', checked: false}, + {id: '3-3-36', label: '黄埔区-36', checked: false}, + ]}, + ], + }] + } + }, + methods: { + openTree: function() { + this.$refs.nextTreeAsyncRef.showTree = true + }, + changeVerify: function(current, chooseList) { + // 注意:返回非空字符串会阻止原有行为,并提示返回的字符串 + // 如果函数体不做return返回值,即验证通过,控件正常处理业务 + console.log('当前变化的数据', current) + console.log('已选择的数据', chooseList) + if(chooseList && chooseList.length > 4) { + + return '最多可以选择4个节点' + } + }, + checkedFunc: function(id) { + if(this.activeId === id) { + this.activeId = ''; + this.$refs.nextTreeRef.checkedFunc(id, false) + } else { + this.activeId = id; + this.$refs.nextTreeRef.checkedFunc(id) + } + }, + function loadData(data) { + const type = data.$type; // 加载类型 + const source = data.source // 源数据 + // 同步实现的代码处理方式 + + if (type === 'nodeLoad') { + const nodeItem = source; + // 同步实现的代码处理方式 可以返回单个子节点的集合也可以返回子孙节点的集合 + // 如果期望子集节点中还存在孙子节点可以打开,请在初始化数据的时候,初始化个空数组的子节点配置值{[this.childrenKey]: []} + + // if(nodeItem && this.localData[nodeItem.id]) { + // return this.localData[nodeItem.id] + // } else { + // return [] + // } + // 异步的代码实现方式 可以返回单个子节点的集合也可以返回子孙节点的集合 + // 如果期望子集节点中还存在孙子节点可以打开,请在初始化数据的时候,初始化个空数组的子节点配置值{[this.childrenKey]: []} + return new Promise((resolve, reject) => { + setTimeout(() => { + if(nodeItem && self.localData[nodeItem.id]) { + return resolve(self.localData[nodeItem.id]) + } else { + return resolve([]) + } + }, 1000) + }) + } else if(type === 'remoteSearch') { // searchModel=remote的时候,会在loadData函数中返回type参数供做业务处理 + // ...doing + } + + }, + + getTitle: function(checked) { + return `已选:${checked.length}项` + }, + echoDefault: function() { + const selectIds = ['2-1','3-3-35'] + this.checkedTreeData(this.treeData, selectIds) + console.log('treeData的数据:', this.treeData) + this.funcMode = 'checkbox' + this.$refs.nextTreeRef.showTree = true + }, + itemclick: function(_multiple, _selectParent, _checkStrictly = false) { + this.funcMode = _multiple ? 'checkbox' : 'radio' + this.selectParent = _selectParent + this.checkStrictly = _checkStrictly + this.$refs.nextTreeRef.showTree = true + }, + checkedTreeData: function(treeData, selectIds) { + // 注意 vue2当数据深嵌套时,如果没有在treeData里面初始化checked属性,那在改变数据的时候直接将checked属性赋值为true,这时候ui界面有可能不会更新, + // 这时候建议使用this.$set去更新checked属性值,或者在初始化this.treeData的时候初始化checked属性 + (treeData || []).map(item => { + if (selectIds.indexOf(item.id) !== -1) { + item.checked = true + } else { + item.checked = false + } + if (item.children && item.children.length) { + this.checkedTreeData(item.children, selectIds) + } + }) + }, + onconfirm: function(list) { + console.log('选中项的数量列表list:', list) + }, + oncancel: function() { + // 清除treeData的选中状态 + this.checkedTreeData(this.treeData, []) + } + }, + created() { + self = this + } +} +</script> +<style lang="scss"> + .line-block { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + .img { + width: 40rpx; + height: 40rpx; + border-radius: 10rpx; + margin: 0 20rpx; + } + } +</style> + +``` + +### 个性化自定义样式渲染 + +如果你的需求对样式需求比较高,请使用插槽模式渲染,本组件提供label插槽供你自定义定制; + +```js +<script> +// 提供参考一组自定义渲染数据demo,treeData如下; +const treeData = [ + {id: '1', label: '北京', prev: 'PonderNext-', iconSrc: 'https://img95.699pic.com/xsj/03/fg/hj.jpg%21/fh/300', children: [ + {id: '1-3-3-1', label: '北京区-1', prev: '前置-'}, + {id: '1-3-3-2', label: '北京区-2', post: '-后置'}, + {id: '1-3-3-3', label: '北京区-3', post: '-后置', prev: '前置-'}, + {id: '1-3-3-4', label: '北京区-4'}, + {id: '1-3-3-5', label: '北京区-5'}, + {id: '1-3-3-6', label: '北京区-6'}, + {id: '1-3-3-7', label: '北京区-7'}, + {id: '1-3-3-8', label: '北京区-8'}, + {id: '1-3-3-9', label: '北京区-9'}, + {id: '1-3-3-10', label: '北京区-10'}, + {id: '1-3-3-12', label: '北京区-11'}, + {id: '1-3-3-13', label: '北京区-12'}, + {id: '1-3-3-13', label: '北京区-13'}, + {id: '1-3-3-14', label: '北京区-14'}, + {id: '1-3-3-15', label: '北京区-15'}, + {id: '1-3-3-16', label: '北京区-16'}, + {id: '1-3-3-17', label: '北京区-17'}, + {id: '1-3-3-18', label: '北京区-18'}, + {id: '1-3-3-19', label: '北京区-19'}, + {id: '1-3-3-20', label: '北京区-20'}, + {id: '1-3-3-21', label: '北京区-21'}, + {id: '1-3-3-22', label: '北京区-22'}, + {id: '1-3-3-23', label: '北京区-23'}, + {id: '1-3-3-24', label: '北京区-24'}, + {id: '1-3-3-25', label: '北京区-25'}, + {id: '1-3-3-26', label: '北京区-26'}, + {id: '1-3-3-27', label: '北京区-27'}, + {id: '1-3-3-28', label: '北京区-28'}, + {id: '1-3-3-29', label: '北京区-29'}, + {id: '1-1-3-3-30', label: '北京区-30'}, + {id: '1-3-3-31', label: '北京区-31'}, + {id: '1-3-3-32', label: '北京区-32'}, + {id: '1-3-3-33', label: '北京区-33'}, + {id: '1-3-3-34', label: '北京区-34'}, + {id: '1-3-3-35', label: '北京区-35'}, + {id: '1-3-3-36', label: '北京区-36'}, + {id: '1-3-3-37', label: '北京区-37'}, + {id: '1-3-3-38', label: '北京区-38'}, + {id: '1-3-3-39', label: '北京区-39'}, + {id: '1-3-3-40', label: '北京区-40'}, + {id: '1-3-3-41', label: '北京区-41'}, + {id: '1-3-3-42', label: '北京区-42'}, + {id: '1-3-3-43', label: '北京区-43'}, + {id: '1-3-3-44', label: '北京区-44'}, + {id: '1-3-3-45', label: '北京区-45'}, + {id: '1-3-3-46', label: '北京区-46'}, + {id: '1-3-3-47', label: '北京区-47'}, + {id: '1-3-3-48', label: '北京区-48'}, + {id: '1-3-3-49', label: '北京区-49'}, + {id: '1-3-3-50', label: '北京区-50'}, + {id: '1-3-3-51', label: '北京区-51'}, + {id: '1-3-3-52', label: '北京区-52'}, + {id: '1-3-3-53', label: '北京区-53'}, + {id: '1-3-3-54', label: '北京区-54'}, + ]}, + {id: '2', label: '上海', prev: 'PonderNext-', iconSrc: 'https://img95.699pic.com/xsj/0g/hb/tc.jpg%21/fh/300', children: [ + {id: '2-1', label: '上海-1', iconSrc: 'https://img1.baidu.com/it/u=1997340124,765201109&fm=253&fmt=auto&app=120&f=JPEG?w=285&h=285'}, + {id: '2-2', label: '上海-2', iconSrc: 'https://img1.baidu.com/it/u=1997340124,765201109&fm=253&fmt=auto&app=120&f=JPEG?w=285&h=285'}, + {id: '2-3', label: '上海-3', iconSrc: 'https://img1.baidu.com/it/u=1997340124,765201109&fm=253&fmt=auto&app=120&f=JPEG?w=285&h=285'}, + ] }, + {id: '3', label: '广州', prev: 'PonderNext-', iconSrc: 'https://storage-public.zhaopin.cn/user/avatar/1589350028141684980/d00a1afa-e3ec-40a5-a68e-aef1f684b189.jpg', children: [ + {id: '3-1', label: '海珠区', iconSrc: 'https://img95.699pic.com/xsj/0u/f3/5h.jpg%21/fh/300', children: [ + {id: '3-1-1', label: '海珠区-1', disabled: true}, + {id: '3-1-2', label: '海珠区-2', post: '-后置', prev: '前置-'}, + {id: '3-1-4', label: '海珠区-3', post: '-后置',}, + {id: '3-1-5', label: '海珠区-4', children: [ + { id: '3-1-5-1', label: '海珠区-4-200号'}, + { id: '3-1-5-2', label: '海珠区-4-201号', children: [ + { id: '3-1-5-1-1', label: '海珠区-4-200号-2'}, + { id: '3-1-5-2-1', label: '海珠区-4-201号-3'}, + ]}, + ]}, + {id: '3-1-6', label: '海珠区-5'}, + {id: '3-1-7', label: '海珠区-6'}, + {id: '3-1-8', label: '海珠区-7', post: '-后置',}, + {id: '3-1-9', label: '海珠区-8'}, + {id: '3-1-10', label: '海珠区-9'}, + {id: '3-1-11', label: '海珠区-10'}, + {id: '3-1-1', label: '海珠区-11', disabled: true}, + {id: '3-1-2', label: '海珠区-12'}, + {id: '3-1-4', label: '海珠区-13'}, + {id: '3-1-5', label: '海珠区-14'}, + {id: '3-1-6', label: '海珠区-15'}, + {id: '3-1-7', label: '海珠区-16'}, + {id: '3-1-8', label: '海珠区-17'}, + {id: '3-1-9', label: '海珠区-18'}, + {id: '3-1-10', label: '海珠区-19', prev: '前置-'}, + {id: '3-1-11', label: '海珠区-20'}, + {id: '3-1-1', label: '海珠区-21', disabled: true}, + {id: '3-1-2', label: '海珠区-22'}, + {id: '3-1-4', label: '海珠区-23'}, + {id: '3-1-5', label: '海珠区-24'}, + {id: '3-1-6', label: '海珠区-25'}, + {id: '3-1-7', label: '海珠区-26'}, + {id: '3-1-8', label: '海珠区-27'}, + {id: '3-1-9', label: '海珠区-28'}, + {id: '3-1-10', label: '海珠区-29'}, + {id: '3-1-11', label: '海珠区-30'}, + {id: '3-1-1', label: '海珠区-31', disabled: true}, + {id: '3-1-2', label: '海珠区-32'}, + {id: '3-1-4', label: '海珠区-33'}, + {id: '3-1-5', label: '海珠区-34'}, + {id: '3-1-6', label: '海珠区-35'}, + {id: '3-1-7', label: '海珠区-36'}, + {id: '3-1-8', label: '海珠区-37'}, + {id: '3-1-9', label: '海珠区-38'}, + {id: '3-1-10', label: '海珠区-39'}, + {id: '3-1-11', label: '海珠区-40'}, + {id: '3-1-1', label: '海珠区-41', disabled: true}, + {id: '3-1-2', label: '海珠区-42'}, + {id: '3-1-4', label: '海珠区-43'}, + {id: '3-1-5', label: '海珠区-44'}, + {id: '3-1-6', label: '海珠区-45'}, + {id: '3-1-7', label: '海珠区-46'}, + {id: '3-1-8', label: '海珠区-47'}, + {id: '3-1-9', label: '海珠区-48'}, + {id: '3-1-10', label: '海珠区-49'}, + {id: '3-1-11', label: '海珠区-50'}, + {id: '3-1-11', label: '海珠区-51'}, + ]}, + {id: '3-2', label: '番禺区', iconSrc: 'https://img1.baidu.com/it/u=931648192,3196263841&fm=253&fmt=auto&app=120&f=JPEG?w=285&h=285', disabled: true, checked: true, children: [ + {id: '3-2-1', label: '番禺区-1'}, + {id: '3-2-2', label: '番禺区-2'}, + {id: '3-2-4', label: '番禺区-3'}, + {id: '3-2-5', label: '番禺区-4'}, + {id: '3-2-6', label: '番禺区-5'}, + {id: '3-2-7', label: '番禺区-6'}, + {id: '3-2-8', label: '番禺区-7'}, + {id: '3-2-9', label: '番禺区-8'}, + {id: '3-2-10', label: '番禺区-9'}, + {id: '3-2-11', label: '番禺区-10'}, + ]}, + {id: '3-3', label: '黄埔区', iconSrc: 'https://img.jiaoyubao.cn/43423/20210423113959473-20210423114005024.jpeg', children: [ + {id: '3-3-1', label: '黄埔区-1'}, + {id: '3-3-2', label: '黄埔区-2'}, + {id: '3-3-3', label: '黄埔区-3'}, + {id: '3-3-4', label: '黄埔区-4'}, + {id: '3-3-5', label: '黄埔区-5'}, + {id: '3-3-6', label: '黄埔区-6'}, + {id: '3-3-7', label: '黄埔区-7'}, + {id: '3-3-8', label: '黄埔区-8'}, + {id: '3-3-9', label: '黄埔区-9'}, + {id: '3-3-10', label: '黄埔区-10'}, + {id: '3-3-12', label: '黄埔区-11'}, + {id: '3-3-13', label: '黄埔区-12'}, + {id: '3-3-13', label: '黄埔区-13'}, + {id: '3-3-14', label: '黄埔区-14'}, + {id: '3-3-15', label: '黄埔区-15'}, + {id: '3-3-16', label: '黄埔区-16'}, + {id: '3-3-17', label: '黄埔区-17'}, + {id: '3-3-18', label: '黄埔区-18'}, + {id: '3-3-19', label: '黄埔区-19'}, + {id: '3-3-20', label: '黄埔区-20'}, + {id: '3-3-21', label: '黄埔区-21'}, + {id: '3-3-22', label: '黄埔区-22'}, + {id: '3-3-23', label: '黄埔区-23'}, + {id: '3-3-24', label: '黄埔区-24'}, + {id: '3-3-25', label: '黄埔区-25'}, + {id: '3-3-26', label: '黄埔区-26'}, + {id: '3-3-27', label: '黄埔区-27'}, + {id: '3-3-28', label: '黄埔区-28'}, + {id: '3-3-29', label: '黄埔区-29'}, + {id: '3-3-30', label: '黄埔区-30'}, + {id: '3-3-31', label: '黄埔区-31'}, + {id: '3-3-32', label: '黄埔区-32'}, + {id: '3-3-33', label: '黄埔区-33'}, + {id: '3-3-34', label: '黄埔区-34'}, + {id: '3-3-35', label: '黄埔区-35'}, + {id: '3-3-36', label: '黄埔区-36'}, + {id: '3-3-37', label: '黄埔区-37'}, + {id: '3-3-38', label: '黄埔区-38'}, + {id: '3-3-39', label: '黄埔区-39'}, + {id: '3-3-40', label: '黄埔区-40'}, + {id: '3-3-41', label: '黄埔区-41'}, + {id: '3-3-42', label: '黄埔区-42'}, + {id: '3-3-43', label: '黄埔区-43'}, + {id: '3-3-44', label: '黄埔区-44'}, + {id: '3-3-45', label: '黄埔区-45'}, + {id: '3-3-46', label: '黄埔区-46'}, + {id: '3-3-47', label: '黄埔区-47'}, + {id: '3-3-48', label: '黄埔区-48'}, + {id: '3-3-49', label: '黄埔区-49'}, + {id: '3-3-50', label: '黄埔区-50'}, + {id: '3-3-51', label: '黄埔区-51'}, + {id: '3-3-52', label: '黄埔区-52'}, + {id: '3-3-53', label: '黄埔区-53'}, + {id: '3-3-54', label: '黄埔区-54'}, + ]}, + ], + }] +</script> +``` + +### 预览 +### +*** + +| 功能预览 | 父子级关联演示 | 全面支持大数据量,子孙节点ui按需渲染(按需渲染数据) | +| :------------------------------------------------------------------: | :------------------------------------------------------------------: | :------------------------------------------------------------------: | +|  |  |  | + + + +| 增强控件交互能力,增加筛选search模式,全面支持大数据量交互 | 超强的样式定制能力,满足你高精美组件的需求 | 打开精美的辅助线模式,让你的控件更加友好 | +| :------------------------------------------------------------------: | :------------------------------------------------------------------: | :------------------------------------------------------------------: | +|  |  |  | + + +| 增加搜索模式searchModel=depHighlight模式,从属高亮显示模式 | 支持异步加载子节点,子树集,ajax远程加载数据等 | 支持不同主题的切换,ui定制更简单 | +| :------------------------------------------------------------------: | :------------------------------------------------------------------: | :------------------------------------------------------------------: | +|  |  |  | + +| 增加验证函数和topBar插槽使得更加容易和组件进行交互 | 增加页面模式,支持整页ui展示模式 | 增加展开模式expandedMode配置,支持单链路展开,理论上支持几万数据量 | +| :------------------------------------------------------------------: | :------------------------------------------------------------------: | :------------------------------------------------------------------: | +| | |  | + + + +### 超集功能预览(增值功能) +### +| 实现tree的功能模式扩展,让你的tree组件实现可编辑态 | +| :--------------------------------------------------------------------: | +| | + +## 参数 +可选参数属性列表 + +|参数名 |说明 |类型 |是否必填 |默认值 |可选值 | +|---- |---- |---- |---- |---- |----------------------- | +|uiMode |ui表现方式;popup<弹窗>, page<页面>;默认是 popup |String |是 |popup |page | +|funcMode |功能模式配置;display<展示模式>, edit<编辑模式>,checkbox<多选模式>, radio<单选模式>;默认是 radio |String |是 |radio |dispaly,edit,checkbox | +|treeData |树源数据列表 |Array |是 |[] |- | +|valueKey |绑定value的键属性(项的唯一key标识) |String |否 |id |- | +|labelKey |用于显示的字段 |String |否 |label |- | +|disabledKey |禁用节点绑定属性 |String |否 |disabled |- | +|childrenKey |子节点绑定属性 (注意:当item[childrenKey]的值设为null时,代表是无下级数据的父节点,即非叶子节点) |String |否 |children |- | +|title | 弹出标题(如果是函数时会返回所选项的值作为回调参数如;title: (checked):String => {}) |String, Function |否 |'' |- | +|selectParent |作用于funcMode=display模式下;是否可以选父级 |Boolean |否 |false |true | +|foldAll| 折叠时关闭所有已经打开的子集,再次打开时需要一级一级打开 |Boolean |否 |false |true | +|themeColor |主题颜色 |String |否 |#f9ae3d |- | +|cancelColor |取消按钮颜色 |String |否 |#757575 |- | +|titleColor |标题颜色 |String |否 |#757575 |- | +|border |是否有分割线 |Boolean |否 |false |true | +|checkStrictly|作用于funcMode=checkbox模式下; 状态下节点选择完全受控(父子节点选中状态不再关联) |Boolean |否 |false |true | +|checkStrictlyModel|作用于funcMode=checkbox模式下;父子节点关联模式:strong:强关联(不再受限节点的disabled控制),weak:弱关联(节点关联受disabled控制) |String |是 |weak |strong | +|showHalfCheckedTips|作用于funcMode=checkbox模式下; checkStrictly为false的状态下生效;父子节点选中状态不再关联,是否展示半选提示; |Boolean |否 |false |true | +|ifSearch| 筛选search模式 |Boolean |否 |true |false | +|searchModel| 搜索模式配置 depHighlight: 从属高亮(显示层级并高亮显示);common: 一般;remote: 远程 |String |否 |common |depHighlight | +|showAuxiliaryLine| 是否打开辅助线 |Boolean |否 |false |true | +|loadData| 异步加载函数 (node):Promise([childData]) => {} // demo有说明 |Function |否 |- |- | +|height| 只在uiMode=popup时生效;弹层容器的高度,默认是500 |Number |否 |500 |- | +|changeVerify|作用于funcMode=display模式下; 验证函数 (current as any, chooseList as any []):String => {} // 验证函数会把当前控件的选择值作为参数返给函数体,demo有说明 |Function |否 |- |- | +|expandedKeys| (Controlled) Specifies the keys of the expanded treeNodes 展开配置项,格式为[valueKey] |Array |否 |[] |- | +|expandedMode| 展开模式配置: common: 一般模式;singe: 单一模式; |String |否 |common |singe | + + +# Event 事件 +|事件名 |说明 |类型 |回调参数 | +|---- |---- |---- |---- | +|confirm|菜单收起时返回的筛选结果 |emit |array | +|clear|点击清除按钮时触发 |emit |- | +|cancel|关闭弹层和点击取消时触发 |emit |- | +|change|选项改变时触发 |emit |array | + +## Slot 插槽 + +|名称 |说明 |参数 | +|---- |---- |---- | +|label |label插槽 |data(当前项对于treeData里面的数据) | +|topBar |topBar插槽 |----滚动区域顶部topBar插槽 | +|bottomBar |bottomBar插槽 |----滚动区域底部bottomBar插槽 | +|fixedBottomBar |fixedBottomBar插槽 |----固定在页面的底部,使用fixed进行定位 | +|empty |empty插槽 |----数据为空的插槽 | + diff --git a/utils/request.js b/utils/request.js index dc97da5..e72ff42 100644 --- a/utils/request.js +++ b/utils/request.js @@ -1,5 +1,5 @@ -// let requestPath = 'http://192.168.0.31:8992/qa-prevention-xgf/'; // 后台请求地址 -let requestPath = 'https://skqhdg.porthebei.com:9006/qa-prevention-xgf/'; // 后台请求地址 +let requestPath = 'http://192.168.0.99:8199/'; // 后台请求地址 +// let requestPath = 'https://skqhdg.porthebei.com:9006/qa-prevention-xgf/'; // 后台请求地址 // let requestPath = 'https://qgxgf.qhdsafety.com/qa-prevention-xgf/'; // 外网地址 import store from '../store/index' @@ -19,16 +19,16 @@ function post(url, data) { }, method: 'POST', header: { - 'Content-type': 'application/x-www-form-urlencoded' + 'Content-type': data?.postMethod || 'application/x-www-form-urlencoded' }, success: (res) => { if (res.statusCode != 200){ uni.showToast({ - title: '网络错误请重试,' +res.statusCode, + title: '网络错误请重试', icon: 'error', duration: 2000 }); - reject(err) + reject(res) } if (data && data.loading !== false) { uni.hideLoading(); diff --git a/utils/submitHomeworkProcess.js b/utils/submitHomeworkProcess.js new file mode 100644 index 0000000..9ccc01a --- /dev/null +++ b/utils/submitHomeworkProcess.js @@ -0,0 +1,76 @@ +import {getCurrentNextOperation, setTaskSave} from "../api/index"; + +// 处理表单数据 +const processFormData = (formData, formItems) => { + const form = [] + for (let i = 0; i < formItems.length; i++) { + const formItem = formItems[i] + form.push({ + ITEM: formItem.name, + ITEM_NAME: formItem.key_name, + ITEM_VALUE: formData[formItem.key_name], + FK_NAME: formItem.key_id || '', + FK_VALUE: formData[formItem.key_id] || '', + TYPE: formItem.type, + SORT: i + 1 + }) + } + return form +} +// 提交表单基础信息 +export const setSubmitForm = async ({form, formItems, TYPE, CORP_ID}) => { + const {taskIds} = await setTaskSave({ + data: [{form: processFormData(form, formItems)}], + TYPE, + postMethod: 'application/json', + }) + await resolveNextOperation(taskIds[0], CORP_ID, TYPE) +} +// 处理选择人员数据 +const processingSelectedPersonnelData = (formData, list) => { + const form = [] + for (let i = 0; i < list.length; i++) { + const item = list[i] + form.push({ + TYPE_NAME: item.desc, + TYPE: item.code, + DEPARTMENT_ID: list[i].type === '1' ? formData[item.key_id] : '', + DEPARTMENT_NAME: list[i].type === '1' ? formData[item.key_name] : '', + USER_ID: list[i].type === '0' ? formData[item.key_id] : '', + USER_NAME: list[i].type === '0' ? formData[item.key_name] : '', + isDepartment: list[i].type + }) + } + return form +} +// 提交人员信息 +export const setPersonnelForm = async ({form, list, taskId, CORP_ID, TYPE}) => { + await setTaskSave({ + data: [{jobs: processingSelectedPersonnelData(form, list), task: {EW_RU_TASK_ID: taskId}}], + postMethod: 'application/json', + }) + await resolveNextOperation(taskId, CORP_ID, TYPE) +} +// 决定下一步操作 EW_RU_TASK_ID当前任务id CORP_ID 选择的企业id TYPE作业类型 +const resolveNextOperation = async (EW_RU_TASK_ID, CORP_ID, TYPE) => { + const {info} = await getCurrentNextOperation({EW_RU_TASK_ID}) + // 选人 + if (info.chooseNextFlag === '1') { + uni.navigateTo({ + url: '/pages/eight_assignments/select_review_personnel', + success: ({eventChannel}) => { + eventChannel.emit('nextNodes', {nextNodes: info.nextNodes, taskId: EW_RU_TASK_ID, CORP_ID, TYPE}) + } + }) + return + } + // 填写安全措施 + if (info.completeFlag === '1') { + return + } + // 填写其它安全措施 + if (info.canAddFlag === '1') { + return + } + console.log('没有下一步了') +}