123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- <template>
- <hc-drawer v-model="isShow" ui="hc-project-list-edit-formula-drawer" to-id="hc-layout-box" is-close @close="drawerClose">
- <hc-card is-action-btn :scrollbar="isScrollBar">
- <div class="hc-project-list-edit-formula-card" :class="isScrollBar ? '' : 'is-no-scroll'">
- <!-- 顶部操作 -->
- <div class="hc-formula-card-box border-dashed-card hc-flex mb-14px h-58px">
- <div class="retain hc-flex h-full w-174px">
- <el-checkbox v-model="isRetain" size="large" />
- <span class="ml-5px text-14px">保留</span>
- <div class="relative ml-5px w-90px">
- <el-input-number v-model="retainNum" block :step="1" :min="0" :max="5" :disabled="!isRetain" controls-position="right" />
- </div>
- <span class="ml-5px text-14px">位</span>
- </div>
- <div class="range hc-flex h-full w-155px">
- <el-button :type="deviationRangeShow ? 'primary' : ''" @click="setDeviationRange">允许偏差值范围</el-button>
- </div>
- <div class="menu h-full flex-1">
- <hc-body padding="0">
- <el-menu :default-active="formulaMenuIndex" mode="horizontal" @select="handleFormulaMenu">
- <el-sub-menu v-for="(arr, key, index) in formulaMenuList" :key="key" :index="key">
- <template #title>{{ key }}</template>
- <el-menu-item v-for="(item, i) in arr" :key="i" :index="`${index + 1}-${i + 1}`">{{ item?.name }}</el-menu-item>
- </el-sub-menu>
- </el-menu>
- </hc-body>
- </div>
- <div class="hand hc-flex h-full w-100px">
- <el-button>手写模式</el-button>
- </div>
- </div>
- <!-- 函数公式 -->
- <div class="border-dashed-card hc-formula-card-math mb-14px">
- <div class="header hc-flex">
- <div class="name flex-1 text-14px">函数公式.</div>
- <div class="extra relative ml-24px">
- <el-button :type="isResetFun ? 'primary' : 'info'" size="small" @click="resetFunClick">重置函数</el-button>
- </div>
- </div>
- <div class="body relative">
- <template v-for="(item, index) in resultFormula" :key="index">
- <span class="element-class text-26px" :class="item.selected ? 'is-cur' : ''" @click="resultFormulaItem(item)">{{ item.name }}</span>
- </template>
- <span class="ml-10px mr-10px text-26px">=</span>
- <template v-for="(item, index) in processFormula" :key="index">
- <span class="element-class text-26px" :class="item.selected ? 'is-cur' : ''" @click="processFormulaItem(item)">{{ item.name }}</span>
- </template>
- </div>
- </div>
- <!-- 重置函数 -->
- <div v-if="isResetFun" class="hc-formula-reset-fun mb-14px">
- <hc-body split padding="0">
- <template #left>
- <hc-card class="reset-fun-left-card" scrollbar :loading="treeResetFunLoading">
- <template #header>
- <hc-search-input v-model="resetFunTree" @search="resetFunTreeSearch" />
- </template>
- <el-tree
- ref="treeResetFunLazyRef" :default-expanded-keys="treeResetFunLazyExpanded" node-key="id" :props="defaultProps"
- :expand-on-click-node="false" lazy highlight-current :load="treeResetFunLazyLoad" @node-click="treeResetFunLazyClick"
- />
- </hc-card>
- </template>
- <hc-card class="reset-fun-right-card">
- <template #header>
- <hc-search-input v-model="resetFunEle" placeholder="请输入你想搜索的元素字段" @search="resetFunEleSearch" />
- </template>
- <div class="body h-full">
- <div class="tag-box">
- <template v-for="(item, index) in resetFunEleData" :key="index">
- <el-button v-if="item.k" type="primary" plain size="small" @click="resetFunEleTagClick(item)">{{ item.name }}</el-button>
- <el-button v-else type="primary" plain size="small" @click="resetFunEleTagClick(item)">{{ item.eName }}</el-button>
- </template>
- </div>
- <div class="action-box hc-flex">
- <div class="left hc-flex flex-1">
- <div class="btn hc-flex-center" @click="resetFunText">
- <span class="text">输入值</span>
- </div>
- <div class="btn hc-flex-center" @click="resetFunBrackets('(')">
- <span class="symbol">(</span>
- </div>
- <div class="btn hc-flex-center" @click="resetFunBrackets(')')">
- <span class="symbol">)</span>
- </div>
- <div class="btn hc-flex-center" @click="resetFunOperator('+')">
- <i class="i-ri-add-line" />
- </div>
- <div class="btn hc-flex-center" @click="resetFunOperator('-')">
- <i class="i-ri-subtract-line" />
- </div>
- <div class="btn hc-flex-center" @click="resetFunOperator('*')">
- <i class="i-ri-close-line" />
- </div>
- <div class="btn hc-flex-center" @click="resetFunOperator('%')">
- <hc-icon name="divide" />
- </div>
- </div>
- <div class="right hc-flex">
- <el-tooltip content="删除元素" placement="top-end">
- <div class="btn hc-flex-center" @click="resetFunDel">
- <i class="i-ri-delete-back-2-line" />
- </div>
- </el-tooltip>
- <el-tooltip content="清空所有" placement="top-end">
- <div class="btn hc-flex-center" @click="resetFunClear">
- <i class="i-ri-delete-bin-3-line" />
- </div>
- </el-tooltip>
- </div>
- </div>
- <div class="input-box">
- <draggable v-model="selectEleFormula" item-key="index">
- <template #item="{ element }">
- <span class="element-class" :class="[`is-${element.type}`, element.selected ? 'is-cur' : '']" @click="selectEleFormulaItem(element)">{{ element.name }}</span>
- </template>
- </draggable>
- </div>
- </div>
- </hc-card>
- </hc-body>
- </div>
- </div>
- <template #action>
- <el-button @click="drawerClose">取消</el-button>
- <el-button type="primary" :loading="submitLoading" @click="submitClick">保存</el-button>
- </template>
- </hc-card>
- </hc-drawer>
- </template>
- <script setup>
- import { nextTick, ref, watch } from 'vue'
- import { useClick } from 'hc-vue3-ui'
- import { ElMessageBox } from 'element-plus'
- import {
- arrIndex, deepClone, getArrValue,
- getObjValue, getRandom, isNullES,
- } from 'js-fast-way'
- import draggable from 'vuedraggable'
- import mainApi from '~api/project/formula'
- import eleApi from '~api/project/element'
- import treeApi from '~api/wbs/tree'
- import priApi from '~api/wbs/private'
- const props = defineProps({
- data: {
- type: Object,
- default: () => ({}),
- },
- })
- //事件
- const emit = defineEmits(['close', 'finish'])
- //双向绑定
- const isShow = defineModel('modelValue', {
- default: false,
- })
- //监听数据
- const dataInfo = ref(props.data)
- watch(() => props.data, (data) => {
- dataInfo.value = getObjValue(data)
- }, { immediate: true, deep: true })
- //监听显示
- watch(isShow, (val) => {
- if (val) getDataApi()
- })
- //基础变量
- const symbolReg = /(\+|-|\*|\/)(.+)/ //加减乘除
- const operatorReg = /^\+|-|\*|%/ //加减乘除
- const startFCRegExp = /^FC\.([a-zA-Z\d]+)\(/ //匹配开始的FC.xxx(
- const isScrollBar = ref(true)
- //获取数据
- const getDataApi = async () => {
- console.log(dataInfo.value)
- getTypeMapApi().then()
- getWbsFormElementData().then()
- }
- //保留位数
- const isRetain = ref(false)
- const retainNum = ref(2)
- //允许偏差值范围
- const deviationRangeShow = ref(false)
- const setDeviationRange = () => {
- deviationRangeShow.value = !deviationRangeShow.value
- }
- //获取顶部菜单数据
- const formulaMenuMap = ref({})
- const formulaMenuIndex = ref(null)
- const formulaMenuList = ref({})
- const getTypeMapApi = async () => {
- const { data } = await mainApi.getTypeMap()
- const res = getObjValue(data)
- formulaMenuList.value = deepClone(res)
- //生成map,方便查找
- for (let key in res) {
- if (typeof(res[key]) === 'object') {
- res[key].forEach((formula)=>{
- formula.template = JSON.parse(formula.template)
- if (operatorReg.test(formula.template.ft)) {
- formulaMenuMap.value[formula.template.ft] = formula
- } else if (startFCRegExp.test(formula.template.ft)) {
- let regRes = formula.template.ft.match(startFCRegExp)
- formulaMenuMap.value[regRes[0]] = formula
- }
- })
- }
- }
- }
- //菜单被选择
- const handleFormulaMenu = (index, path) => {
- console.log(index, path)
- }
- //获取数据
- const resultFormula = ref([])
- const getWbsFormElementData = async () => {
- const { eleId } = getObjValue(dataInfo.value)
- resultFormula.value = []
- if (isNullES(eleId)) return
- const { data } = await eleApi.detail(eleId)
- const obj = getObjValue(data)
- resultFormula.value = [{
- type: 'Element',
- name: obj.eName,
- id: obj.id,
- selected: false,
- tableElementKey: obj.tableElementKey,
- children: [],
- }]
- }
- //被点击
- const resultFormulaItem = (item) => {
- //清除右边的选中
- processFormula.value.forEach((obj) => {
- obj.selected = false
- })
- //遍历左边的选中
- const arr = resultFormula.value
- for (let i = 0; i < arr.length; i++) {
- if (item.index === arr[i].index) {
- arr[i].selected = !arr[i].selected
- } else {
- arr[i].selected = false
- }
- }
- resultFormula.value = arr
- }
- //是否重置函数
- const isResetFun = ref(false)
- const resetFunClick = () => {
- isResetFun.value = !isResetFun.value
- isScrollBar.value = !isResetFun.value
- }
- //tree树配置
- const defaultProps = {
- label: 'title',
- children: 'children',
- isLeaf: (data) => {
- return !data.hasChildren
- },
- }
- //重置函数懒加载树
- const treeResetFunLazyRef = ref(null)
- const treeResetFunLazyExpanded = ref([])
- //获取重置函数懒加载树的数据
- const treeResetFunLoading = ref(false)
- const treeResetFunLazyLoad = async (node, resolve) => {
- const { level, data } = node
- let parentId = level !== 0 ? data.id : 12345678910
- const { eleType, node: dataNode, tableType } = getObjValue(dataInfo.value)
- const treeNode = getObjValue(dataNode)
- if (level === 0) treeResetFunLoading.value = true
- if (!eleType) {
- //获取接口数据
- const { data } = await priApi.tabTypeLazyTreeAll({
- parentId,
- current: 1,
- size: 99999,
- hasPartFormula: treeNode.hasPartFormula,
- })
- const res = getArrValue(data.records)
- treeResetFunLoading.value = false
- resolve(res)
- //处理返回的数据
- await nextTick()
- //处理展开
- try {
- const expandId = tableType ? Number(treeNode.tableType) - 1 : Number(treeNode.parentId) - 1
- if (!isNullES(expandId) && expandId >= 0) node.childNodes[expandId].expand()
- } catch { /* empty */ }
- //处理选中
- try {
- const paramsId = treeNode.initTableId
- if (!isNullES(paramsId)) {
- treeResetFunLazyRef.value?.setCurrentKey(paramsId)
- }
- } catch { /* empty */ }
- //获取节点详情
- await getNodeDetailApi(treeNode)
- } else if (eleType) {
- resolve([])
- }
- }
- //重置函数懒加载树点击
- const treeResetFunItemData = ref({})
- const treeResetFunNodeData = ref({})
- const treeResetFunLazyClick = async (data, node) => {
- treeResetFunItemData.value = data
- treeResetFunNodeData.value = node
- await getNodeDetailApi(data)
- }
- //重置函数树搜索
- const resetFunTree = ref('')
- const resetFunTreeSearch = () => {
- }
- //重置函数元素搜索
- const resetFunEle = ref('')
- const resetFunEleSearch = () => {
- }
- //获取节点详情
- const resetFunEleData = ref([])
- const getNodeDetailApi = async (item) => {
- await useClick(300)
- if (isNullES(item.initTableId)) {
- resetFunEleData.value = []
- return
- }
- if (item.hasChildren === false || item.isLinkTable === 2) {
- const { data } = await treeApi.getTableElments({
- id: item.initTableId,
- })
- resetFunEleData.value = getArrValue(data)
- } else {
- resetFunEleData.value = []
- }
- }
- //重置函数 元素字段被点击
- const selectEleFormula = ref([])
- const resetFunEleTagClick = (item) => {
- const arr = selectEleFormula.value
- let { type, name } = getObjValue(arr[arr.length - 1])
- if (type === 'Text') {
- window?.$message.warning('元素无法连续出现在输入值后面')
- return
- }
- if (type === 'Brackets' && name === ')') {
- window?.$message.warning('元素无法连续出现在右括号后面')
- return
- }
- if (arr.length === 0 || ['Operator', 'Brackets'].includes(type) || name === '(') {
- if (!isNullES(item.tableElementKey)) {
- selectEleFormula.value.push({
- type: 'Element',
- name: item.eName,
- id: item.id,
- selected: false,
- tableElementKey: item.tableElementKey,
- index: getRandom(5),
- children: [],
- })
- }
- } else {
- window?.$message.warning('当前操作不符合要求')
- }
- }
- //输入弹窗
- const promptMessageBox = async () => {
- return new Promise(resolve => {
- ElMessageBox.prompt('', '请输入值', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- inputErrorMessage: '请输入内容',
- inputValidator: (value) => {
- return !isNullES(value)
- },
- }).then(({ value }) => {
- resolve(value)
- }).catch(() => {
- resolve()
- })
- })
- }
- //重置函数 输入值
- const resetFunText = async () => {
- const val = await promptMessageBox()
- if (isNullES(val)) return
- const arr = selectEleFormula.value
- if (arr.length > 0) {
- let { type, name } = getObjValue(arr[arr.length - 1])
- if (type === 'Element') {
- window?.$message.warning('输入值无法连续出现在元素后面')
- return
- }
- if (type === 'Text') {
- window?.$message.warning('输入值无法连续出现在输入值后面')
- return
- }
- if (type === 'Brackets' && name === ')') {
- window?.$message.warning('输入值无法连续出现在右括号后面')
- return
- }
- }
- selectEleFormula.value.push({
- type: 'Text',
- name: val,
- selected: false,
- index: getRandom(5),
- })
- }
- //重置函数 括号
- const resetFunBrackets = (val) => {
- selectEleFormula.value.push({
- type: 'Brackets',
- name: val,
- selected: false,
- index: getRandom(5),
- })
- }
- //重置函数 运算符
- const resetFunOperator = (val) => {
- const arr = selectEleFormula.value, map = formulaMenuMap.value
- if (arr.length <= 0) {
- window?.$message.warning('公式开头不能是运算符号')
- return
- }
- let { type, name } = getObjValue(arr[arr.length - 1])
- if (type === 'Operator') {
- window?.$message.warning('运算符号无法连续出现在运算符号后面')
- return
- }
- if (type === 'Brackets' && name === '(') {
- window?.$message.warning('运算符号无法连续出现在左括号后面')
- return
- }
- const obj = getObjValue(map[val])
- selectEleFormula.value.push({
- type: 'Operator',
- name: symbolReg.exec(obj.name)[1],
- selected: false,
- template: obj.template,
- index: getRandom(5),
- })
- }
- //重置函数 删除
- const resetFunDel = () => {
- const arr = deepClone(selectEleFormula.value)
- if (arr.length <= 0) {
- window?.$message.warning('请先添加相关元素')
- return
- }
- const index = arrIndex(arr, 'selected', true)
- if (index !== -1) {
- arr.splice(index, 1)
- selectEleFormula.value = arr
- } else {
- arr.splice(arr.length - 1, 1)
- selectEleFormula.value = arr
- }
- }
- //重置函数 清空
- const resetFunClear = () => {
- selectEleFormula.value = []
- }
- //被点击
- const selectEleFormulaItem = (item) => {
- const arr = selectEleFormula.value
- for (let i = 0; i < arr.length; i++) {
- if (item.index === arr[i].index) {
- arr[i].selected = !arr[i].selected
- } else {
- arr[i].selected = false
- }
- }
- selectEleFormula.value = arr
- }
- //保存
- const submitLoading = ref(false)
- const submitClick = async () => {
- //重置函数
- if (isResetFun.value) {
- setProcessFormula()
- }
- }
- //赋值给等号右边的数组
- const processFormula = ref([])
- const setProcessFormula = () => {
- const arr = selectEleFormula.value
- let leftNum = 0, rightNum = 0
- arr.forEach(({ type, name }) => {
- if (type === 'Brackets') {
- if (name === '(') {
- leftNum++
- } else if (name === ')') {
- rightNum++
- }
- }
- })
- if (leftNum !== rightNum) {
- window?.$message.warning('左右括号数量不相等,请先检查是否正确')
- return
- }
- processFormula.value = deepClone(arr)
- isResetFun.value = false
- isScrollBar.value = true
- }
- //右边被点击
- const processFormulaItem = (item) => {
- //清除左边边的选中
- resultFormula.value.forEach((obj) => {
- obj.selected = false
- })
- //遍历右边的选中
- const arr = processFormula.value
- for (let i = 0; i < arr.length; i++) {
- if (item.index === arr[i].index) {
- arr[i].selected = !arr[i].selected
- } else {
- arr[i].selected = false
- }
- }
- processFormula.value = arr
- }
- //关闭抽屉
- const drawerClose = () => {
- isShow.value = false
- isResetFun.value = false
- isScrollBar.value = true
- selectEleFormula.value = []
- emit('close')
- }
- </script>
- <style lang="scss">
- @import '~src/styles/view/project/edit-formula';
- </style>
|