123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886 |
- <template>
- <!--补卡审批申请-->
- <view class="card-replacement">
- <view class="main">
- <view class="header">
- <view
- v-for="(item, idx) in detailInfo.checkNoTime"
- :key="idx"
- >
- {{ item }} 缺卡
- </view>
- <view>{{ detailInfo.applyNum }}</view>
- </view>
- <u--form
- labelPosition="left"
- :model="fmData"
- :rules="rules"
- ref="uForm"
- labelWidth="auto"
- class="dynamic-form"
- >
- <!--不可新增fmItem-->
- <view class="def-box">
- <u-form-item
- :key="item.id"
- v-for="item in defList"
- :label="item.name"
- :prop="item.valueKey"
- :required="!!item.isRequire"
- borderBottom
- >
- <!-- text -->
- <c-input
- v-if="item.type === typeKeys.text"
- :placeholder="item.tips"
- class="input-text"
- type="text"
- v-model="fmData[item.valueKey]"
- border="none"
- ></c-input>
- <!-- number -->
- <c-input-number
- v-if="item.type === typeKeys.number"
- class="input-text"
- v-model="fmData[item.valueKey]"
- :placeholder="item.tips"
- border="none"
- ></c-input-number>
- <!-- phoneNum -->
- <c-input
- v-if="item.type === typeKeys.phoneNum"
- class="input-text"
- type="number"
- :placeholder="item.tips"
- maxlength="11"
- v-model="fmData[item.valueKey]"
- border="none"
- ></c-input>
- <!-- select -->
- <view
- class="align-right"
- v-if="item.type === typeKeys.select"
- >
- <pickerSelect
- v-model="fmData[item.valueKey]"
- :placeholder="item.tips || '请选择'"
- :columns="item.variables"
- ></pickerSelect>
- </view>
- <!-- radio -->
- <u-radio-group
- v-if="item.type === typeKeys.radio"
- v-model="fmData[item.valueKey]"
- >
- <u-radio
- :customStyle="{
- marginBottom: '8px',
- marginTop: '8px',
- marginRight: '8px',
- }"
- v-for="(item, index) in item.variables"
- :key="index"
- :label="item.name"
- :name="item.name"
- >
- </u-radio>
- </u-radio-group>
- <!-- checkbox -->
- <u-checkbox-group
- v-if="item.type === typeKeys.checkbox"
- v-model="fmData[item.valueKey]"
- >
- <u-checkbox
- :customStyle="{
- marginBottom: '8px',
- marginTop: '8px',
- marginRight: '8px',
- }"
- v-for="(item, index) in item.variables"
- :key="index"
- :label="item.name"
- :name="item.name"
- >
- </u-checkbox>
- </u-checkbox-group>
- <!-- upload -->
- <view
- v-if="item.type === typeKeys.upload"
- class="align-right"
- >
- <c-upload
- :accept="item.accept"
- :maxCount="item.uploadNum"
- v-model="fmData[item.valueKey]"
- >
- </c-upload>
- </view>
- <!-- textarea -->
- <u--textarea
- v-if="item.type === typeKeys.textarea"
- v-model="fmData[item.valueKey]"
- :placeholder="item.tips"
- ></u--textarea>
- <!-- time -->
- <view
- class="align-right"
- v-if="item.type === typeKeys.time"
- >
- <dateSelect
- mode="time"
- v-model="fmData[item.valueKey]"
- @change="dateChange($event, item)"
- :placeholder="item.tips || '请选择'"
- ></dateSelect>
- </view>
- <!-- date -->
- <view
- class="align-right"
- v-if="item.type === typeKeys.date"
- >
- <dateSelect
- mode="date"
- @change="dateChange($event, item)"
- v-model="fmData[item.valueKey]"
- :placeholder="item.tips || '请选择'"
- ></dateSelect>
- </view>
- <!-- datetime -->
- <view
- class="align-right"
- :class="{ 'place-color': !fmData[item.valueKey] }"
- v-if="item.type === typeKeys.datetime"
- >
- <uni-datetime-picker
- v-model="fmData[item.valueKey]"
- :border="false"
- type="datetime"
- @change="dateChange($event, item)"
- />
- </view>
- <!-- datetimerange -->
- <view
- class="align-right"
- :class="{ 'place-color': !fmData[item.valueKey] }"
- v-if="item.type === typeKeys.datetimerange"
- >
- <uni-datetime-picker
- v-model="fmData[item.valueKey]"
- :border="false"
- type="datetimerange"
- @change="dateChange($event, item)"
- />
- </view>
- </u-form-item>
- </view>
- <!--可新增fmItem-->
- <view class="add-group" :key="i" v-for="(arr, i) in addList">
- <view class="add-item-header">
- <view class="hd-left-title">序列</view>
- <c-button @click="delArr(i)" type="text">删除</c-button>
- </view>
- <u-form-item
- :label="item.name"
- :prop="item.valueKey"
- :required="!!item.isRequire"
- :key="item.id"
- v-for="item in arr"
- borderBottom
- >
- <!-- text -->
- <c-input
- v-if="item.type === typeKeys.text"
- :placeholder="item.tips"
- class="input-text"
- type="text"
- v-model="fmData[item.valueKey]"
- border="none"
- ></c-input>
- <!-- number -->
- <c-input
- v-if="item.type === typeKeys.number"
- class="input-text"
- type="number"
- v-model="fmData[item.valueKey]"
- border="none"
- ></c-input>
- <!-- phoneNum -->
- <c-input
- v-if="item.type === typeKeys.phoneNum"
- class="input-text"
- type="number"
- :placeholder="item.tips"
- maxlength="11"
- v-model="fmData[item.valueKey]"
- border="none"
- ></c-input>
- <!-- select -->
- <view
- class="align-right"
- v-if="item.type === typeKeys.select"
- >
- <pickerSelect
- v-model="fmData[item.valueKey]"
- :placeholder="item.tips || '请选择'"
- :columns="item.variables"
- ></pickerSelect>
- </view>
- <!-- radio -->
- <u-radio-group
- v-if="item.type === typeKeys.radio"
- v-model="fmData[item.valueKey]"
- >
- <u-radio
- :customStyle="{
- marginBottom: '8px',
- marginTop: '8px',
- marginRight: '8px',
- }"
- v-for="(item, index) in item.variables"
- :key="index"
- :label="item.name"
- :name="item.name"
- >
- </u-radio>
- </u-radio-group>
- <!-- checkbox -->
- <u-checkbox-group
- v-if="item.type === typeKeys.checkbox"
- v-model="fmData[item.valueKey]"
- >
- <u-checkbox
- :customStyle="{
- marginBottom: '8px',
- marginTop: '8px',
- marginRight: '8px',
- }"
- v-for="(item, index) in item.variables"
- :key="index"
- :label="item.name"
- :name="item.name"
- >
- </u-checkbox>
- </u-checkbox-group>
- <!-- upload -->
- <view
- v-if="item.type === typeKeys.upload"
- class="align-right"
- >
- <c-upload
- :accept="item.accept"
- :maxCount="item.uploadNum"
- v-model="fmData[item.valueKey]"
- >
- </c-upload>
- </view>
- <!-- textarea -->
- <u--textarea
- v-if="item.type === typeKeys.textarea"
- v-model="fmData[item.valueKey]"
- :placeholder="item.tips"
- ></u--textarea>
- <!-- time -->
- <view
- class="align-right"
- v-if="item.type === typeKeys.time"
- >
- <dateSelect
- mode="time"
- v-model="fmData[item.valueKey]"
- @change="dateChange($event, item, arr)"
- :placeholder="item.tips || '请选择'"
- ></dateSelect>
- </view>
- <!-- date -->
- <view
- class="align-right"
- v-if="item.type === typeKeys.date"
- >
- <dateSelect
- mode="date"
- @change="dateChange($event, item, arr)"
- v-model="fmData[item.valueKey]"
- :placeholder="item.tips || '请选择'"
- ></dateSelect>
- </view>
- <!-- datetime -->
- <view
- class="align-right"
- :class="{
- 'place-color': !fmData[item.valueKey],
- }"
- v-if="item.type === typeKeys.datetime"
- >
- <uni-datetime-picker
- v-model="fmData[item.valueKey]"
- :border="false"
- type="datetime"
- @change="dateChange($event, item, arr)"
- />
- </view>
- <!-- datetimerange -->
- <view
- class="align-right"
- :class="{
- 'place-color': !fmData[item.valueKey],
- }"
- v-if="item.type === typeKeys.datetimerange"
- >
- <uni-datetime-picker
- v-model="fmData[item.valueKey]"
- :border="false"
- type="datetimerange"
- @change="dateChange($event, item, arr)"
- />
- </view>
- </u-form-item>
- <view
- class="add-item-footer"
- v-if="i === addList.length - 1"
- >
- <c-button size="mini" type="primary" @click="handleAdd">
- <u-icon name="plus" color="#fff" size="12"></u-icon>
- <text style="margin-left: 4px">增加</text>
- </c-button>
- </view>
- </view>
- </u--form>
- <!--流程图-->
- <footer-process
- :condList="condList"
- :infoDto="infoDto"
- ></footer-process>
- </view>
- <view class="footer">
- <c-button
- :disabled="disabled"
- @click="handleSubmit"
- type="primary"
- >
- 提交
- </c-button>
- </view>
- </view>
- </template>
- <script>
- import { typeKeys } from '@/pages/approval/types';
- import pickerSelect from '@/pages/approval/com/pickerSelect.vue';
- import dateSelect from '@/pages/approval/com/dateSelect.vue';
- import { process, start, priview } from '@/api/approval/index.js';
- import FooterProcess from '@/pages/approval/approvalApply/footerProcess.vue';
- export default {
- name: 'cardReplacement',
- props: {
- processType: String, // 流程类型
- },
- components: {
- FooterProcess,
- pickerSelect,
- dateSelect,
- },
- data() {
- return {
- condList: [], // 作为条件的项
- orderId: '',
- detailInfo: {
- checkNoTime: [], // tips
- elementBos: [],
- },
- infoDto: [], // 流程图
- typeKeys,
- fmData: {},
- rules: {},
- defList: [], // 默认项(不可新增)
- addList: [], // 新增项 二维数组[[]]
- };
- },
- computed: {
- disabled() {
- return (
- this.detailInfo.checkNoTime.length === 0 &&
- this.processType === '0' // 0补卡审批
- );
- },
- },
- created() {
- this.getForms();
- },
- methods: {
- watchList() {
- // 监听作为条件的项
- const { condList } = this;
- if (condList.length === 0) {
- return;
- }
- // 筛选出所有必填的 作为条件项
- const reqList = condList.filter((item) => item.isRequire === 1);
- condList.forEach((item) => {
- const { valueKey } = item;
- let val = this.getDefaultVal(item); // 获取默认数据
- Object.defineProperty(this.fmData, valueKey, {
- get: () => {
- return val;
- },
- set: (newVal) => {
- if (val === newVal) return;
- val = newVal;
- const isErr = reqList.some((it) => {
- const k = it.valueKey;
- return !this.fmData[k] && this.fmData[k] !== 0;
- }); // false:已全部填写完整
- const { text, number } = typeKeys;
- if (![text, number].includes(item.type)) {
- this.$forceUpdate();
- }
- if (!isErr) {
- uni.$u.debounce(this.getFlowChart, 300); // 防抖
- }
- },
- });
- });
- },
- async getFlowChart() {
- const params = {};
- this.condList.forEach((item) => {
- const key = item.valueKey;
- params[key] = this.fmData[key];
- });
- const { data } = await priview(this.orderId, params);
- this.infoDto = data;
- },
- delArr(idx) {
- uni.showModal({
- content: '确认删除吗?',
- success: (e) => {
- if (e.confirm) {
- this.addList.splice(idx, 1);
- }
- },
- });
- },
- handleAdd() {
- const firstArr = this.addList[0];
- const rdm = Math.random().toString(36).slice(-6);
- const newArr = firstArr.map((item) => {
- const valueKey = `${item.name}${rdm}`;
- this.setFmRule(item, valueKey); // 设置fm某一项的校验规则
- return {
- ...item,
- valueKey,
- };
- });
- this.addList.push(newArr);
- },
- getDefaultVal(item) {
- // 获取默认数据类型
- return item.type === typeKeys.checkbox ? [] : undefined;
- },
- setDefList(data) {
- // 不是新增项
- data.forEach((item) => {
- const valueKey = item.name;
- this.setFmRule(item, valueKey); // 设置fm某一项的校验规则
- this.$set(this.fmData, valueKey, this.getDefaultVal(item)); // 确保数据响应式
- const variables = item.variables
- ? JSON.parse(item.variables)
- : []; // 处理variables为array
- this.defList.push({ ...item, variables, valueKey });
- });
- },
- setFmRule(item, valueKey) {
- // 设置fm某一项的校验规则
- if (item.isRequire !== 1) {
- // ====1必填
- return;
- }
- const { checkbox, upload, number } = this.typeKeys;
- const ruleType = {
- [checkbox]: 'array',
- [upload]: 'array',
- [number]: 'number',
- };
- const rule = [
- {
- type: ruleType[item.type] || 'string',
- required: true,
- message: '该项必填',
- trigger: ['change'],
- },
- ];
- // 必填
- this.$set(this.rules, valueKey, rule);
- },
- setAddList(data) {
- if (!data || data.length === 0) {
- return;
- }
- // 可新增项
- const newData = data.map((item) => {
- const valueKey = `${item.name}0`;
- this.setFmRule(item, valueKey); // 设置fm某一项的校验规则
- this.$set(this.fmData, valueKey, this.getDefaultVal(item)); // 确保数据响应式
- const variables = item.variables
- ? JSON.parse(item.variables)
- : []; // 处理variables为array
- return { ...item, variables, valueKey };
- });
- this.addList = [newData];
- },
- /**
- * 日期时间范围选择 计算得到小时
- */
- dateTimeRangeChange(dtKey, calcItem) {
- const [startDate, endDate] = this.fmData[dtKey];
- const sumMilli = +new Date(endDate) - +new Date(startDate);
- const h = sumMilli / 1000 / 60 / 60;
- this.setCount(h, calcItem.countkey); // 为计算结果赋值
- },
- getCalcItem(addRrr) {
- const list = addRrr || this.defList;
- const calcItem = {
- countkey: '', // 总和的key
- dtKey: '', // 日期时间范围选择器的key 是否计算
- computeType: [], // 减数/被减数的类型
- rduKey: '', // 1减数的key
- miuKey: '', // 0被减数的key
- };
- list.forEach((item) => {
- const { valueKey } = item;
- if (item.isCompute === 1) {
- // 1是 0否 int
- // 日期时间范围选择器 是否计算 1是 0否 int
- calcItem.dtKey = valueKey;
- }
- if (item.isCount === 1) {
- // 减数
- calcItem.rduKey = valueKey;
- calcItem.computeType.push(item.type);
- }
- if (item.isCount === 0) {
- // 0被减数的key
- calcItem.miuKey = valueKey;
- calcItem.computeType.push(item.type);
- }
- if (item.getCount === 1) {
- // 获取计算结果 1是 0否 int
- calcItem.countkey = valueKey;
- }
- });
- return calcItem;
- },
- dateChange(val, item, addArr) {
- const calcItem = this.getCalcItem(addArr);
- const { rduKey, miuKey, countkey, computeType, dtKey } =
- calcItem;
- const [rType, mType] = computeType || [];
- if (
- countkey &&
- dtKey &&
- item.type === this.typeKeys.datetimerange
- ) {
- // (计算结果key、总和key)存在
- this.dateTimeRangeChange(dtKey, calcItem); // 单独处理日期时间范围选择器
- return;
- }
- if (
- rduKey && // 减数key存在
- miuKey && // 被减数key 存在
- countkey && // 计算结果key存在
- computeType.length === 2 && // 减数和被减数的类型都有
- rType === mType && // 减数和被减数的类型相同
- this.fmData[rduKey] && // 表单中数据选了减数
- this.fmData[miuKey] && //表单中数据选了被减数
- [rduKey, miuKey].includes(item.valueKey) // 是 减数或者被减数
- ) {
- this.calcType(rType, calcItem);
- }
- },
- /**
- * 根据类型进不同函数
- * @param {type} 选择器的类型
- */
- calcType(type, calcItem) {
- // 根据类型进不同函数
- const { time, date, datetime } = this.typeKeys;
- if (type === time) {
- // 类型为time 得到小时
- this.timeCalc(calcItem);
- return;
- }
- if (type === date) {
- // 类型为date 日期-计算得到天
- this.dateCalc(calcItem);
- return;
- }
- if (type === datetime) {
- // 类型为datetime 日期-计算得到小时
- this.dateTimeCalc(calcItem);
- return;
- }
- },
- dateTimeCalc(calcItem) {
- // dateTime计算
- const { rduKey, miuKey, countkey } = calcItem;
- const rduDateTime = this.fmData[rduKey]; //减数
- const miuDateTime = this.fmData[miuKey]; //被减数
- const sumMilli =
- +new Date(rduDateTime) - +new Date(miuDateTime);
- const h = sumMilli / 1000 / 60 / 60;
- this.setCount(h, countkey); // 为计算结果赋值
- },
- /**
- * 日期yyyy-mm-dd 计算得到天
- */
- dateCalc(calcItem) {
- // 日期yyyy-mm-dd 计算得到天
- // 相差: 天 参数--day
- const { rduKey, miuKey, countkey } = calcItem;
- const rduDate = this.fmData[rduKey]; //减数
- const miuDate = this.fmData[miuKey]; //被减数
- const sumMilli = +new Date(rduDate) - +new Date(miuDate);
- const day = sumMilli / 1000 / 60 / 60 / 24;
- this.setCount(day, countkey); // 为计算结果赋值
- },
- setCount(num, countkey) {
- const res = Number(num).toFixed(2) * 1;
- this.fmData[countkey] = res; // 计算结果赋值
- if (this.processType === '2') {
- // 类型是出差
- const dayKey = this.defList.find(
- (item) => item.name === '出差天数'
- )?.valueKey; // 出差天数 fmkey
- const cKeys = this.addList.map((arr) => {
- return arr.find((item) => item.getCount === 1)?.valueKey; // 总和
- }); // 每个行程的时长的key
- if (dayKey && cKeys?.length) {
- let totalDays = 0;
- cKeys.forEach((key) => {
- totalDays += this.fmData[key];
- });
- this.fmData[dayKey] = totalDays;
- }
- }
- },
- /**
- * time计算
- */
- timeCalc(calcItem) {
- const { rduKey, miuKey, countkey } = calcItem;
- // time计算
- const [rduH, rudM] = this.fmData[rduKey].split(':'); //减数
- const [miuH, miuM] = this.fmData[miuKey].split(':'); //被减数
- const rduTotal = rduH * 60 + Number(rudM); // 总分钟-减数
- const miuTotal = miuH * 60 + Number(miuM); // 总分钟-被减数
- const minuteSum = rduTotal - miuTotal; // 得到
- const num = minuteSum / 60;
- this.setCount(num, countkey); // 为计算结果赋值
- },
- async handleSubmit() {
- await this.$refs.uForm.validate();
- try {
- uni.$c.loading();
- const { orderId, resultId } = this.detailInfo || {};
- const { fmData, defList, addList } = this;
- const data = {
- orderId,
- resultId,
- variables: { fmData, defList, addList },
- };
- await start(data);
- uni.hideLoading();
- await uni.$c.toast({
- icon: 'success',
- title: '提交成功',
- });
- // uni.switchTab({ url: '/pages/home/index' }); // 去首页
- this.getForms(); // 刷新
- } catch (e) {
- uni.hideLoading();
- throw new Error(e);
- }
- },
- async getForms() {
- try {
- this.defList = []; // 默认项(不可新增)
- this.addList = []; // 新增项
- uni.$c.loading();
- const { data } = await process({ type: this.processType });
- uni.hideLoading();
- this.orderId = data.orderId;
- const newData = {
- ...data,
- elementBos: data.elementBos || [], //表单
- checkNoTime: data.checkNoTime || [], // 缺卡arr
- };
- //新增项
- const addList = newData.elementBos.filter(
- (item) => item.isAdd === 1
- );
- this.setAddList(addList);
- // 不是新增项
- const defList = newData.elementBos.filter(
- (item) => item.isAdd !== 1
- );
- this.setDefList(defList);
- // todo 查询流程图业务暂不需要判断addList (新增项)
- const condList = this.defList.filter(
- (item) => item.isCondition === 1
- ); // 作为条件的项--全部有值后查询流程图
- this.condList = condList || [];
- if (this.condList.length === 0) {
- this.getFlowChart(); // 如果没有作为条件项--查询流程图
- }
- this.watchList(); // 监听作为条件的项
- this.detailInfo = newData;
- } catch (e) {
- uni.hideLoading();
- throw e;
- }
- },
- },
- };
- </script>
- <style scoped lang="scss">
- .card-replacement {
- display: flex;
- flex-direction: column;
- padding: 30rpx;
- flex: 1;
- overflow: hidden;
- .main {
- flex: 1;
- overflow-y: auto;
- .header {
- font-size: 12px;
- color: #989ca5;
- margin-bottom: 20rpx;
- }
- .dynamic-form {
- .def-box {
- background-color: #fff;
- padding: 0 20rpx;
- border-radius: 6px;
- }
- .add-group {
- background-color: #fff;
- padding: 20rpx;
- border-radius: 6px;
- margin-top: 20rpx;
- .add-item-header {
- display: flex;
- justify-content: space-between;
- background: rgba(67, 111, 246, 0.12);
- line-height: 60rpx;
- border-radius: 4px;
- padding: 0 6px;
- .hd-left-title {
- font-size: 14px;
- color: $u-tips-color;
- }
- }
- .add-item-footer {
- margin-top: 20rpx;
- .c-button {
- width: 70px;
- border-radius: 13px;
- }
- }
- }
- .uni-date {
- width: initial;
- flex: inherit;
- }
- .place-color {
- /deep/.uni-date__x-input {
- color: #c0c4cc;
- }
- }
- .align-right {
- width: 100%;
- display: flex;
- justify-content: flex-end;
- flex-wrap: wrap;
- }
- .u-radio-group {
- flex-wrap: wrap;
- justify-content: flex-end;
- width: 100%;
- }
- .u-checkbox-group {
- flex-wrap: wrap;
- justify-content: flex-end;
- width: 100%;
- }
- .input-text {
- /deep/ .uni-input-input {
- text-align: right;
- }
- /deep/ .uni-input-placeholder {
- text-align: right;
- }
- }
- /deep/ .u-form-item__body {
- display: flex;
- justify-content: space-between;
- .u-form-item__body__right {
- margin-left: 20rpx;
- }
- }
- }
- }
- .footer {
- margin-top: 20rpx;
- margin-bottom: 20rpx;
- }
- }
- </style>
|