| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542 |
- <!--
- author:luoaoxuan
- subject:设施环境通用组件
- -->
- <template>
- <div class="ficily-data">
- <el-row type="flex">
- <el-col style="margin: 0 0 5px 0">
- <div class="button">
- <el-button
- v-if="!isCul"
- type="danger"
- size="mini"
- icon="ibps-icon-close"
- @click="goRemove"
- >删除</el-button
- >
- <el-button
- v-if="!isCul"
- type="success"
- size="mini"
- icon="ibps-icon-plus"
- @click="goAdd"
- >添加</el-button
- >
- </div>
- </el-col>
- </el-row>
- <el-row type="flex">
- <el-col>
- <el-table
- :data="forms"
- border
- @selection-change="handleSelectionChange"
- >
- <el-table-column width="50" type="selection" />
- <el-table-column label="参数名称" prop="label">
- <template slot-scope="{ row }">
- <el-input
- v-if="!readonly && !isCul"
- v-model="row.label"
- size="mini"
- placeholder="请输入"
- />
- <span v-else>{{ row.label || '/' }}</span>
- </template>
- </el-table-column>
- <el-table-column label="参数单位" prop="unit" width="120">
- <template slot-scope="{ row }">
- <el-autocomplete
- v-if="!readonly && !isCul"
- v-model="row.unit"
- class="inline-input"
- :fetch-suggestions="querySearch"
- placeholder="请输入"
- size="mini"
- />
- <span v-else>{{ row.unit || '/' }}</span>
- </template>
- </el-table-column>
- <el-table-column label="参数范围限值" prop="range" width="300">
- <template slot-scope="{ row }">
- <NumberRange
- v-model="row.range"
- :precision="2"
- :disabled="readonly || isCul"
- /></template>
- </el-table-column>
- <el-table-column label="参数修正值" prop="fixValue" width="120">
- <template slot-scope="{ row }">
- <el-input
- v-if="!readonly && !isCul"
- v-model="row.fixValue"
- size="mini"
- placeholder="请输入"
- type="number"
- />
- <span v-else>{{ row.fixValue || '/' }}</span>
- </template>
- </el-table-column>
- <el-table-column label="填写频率" prop="rate">
- <template slot="header" slot-scope="">
- <el-tooltip
- class="item"
- effect="dark"
- content="配置参数填写周期,适用于参数填写频率与监测周期不一致的情况。若为空则表示该参数每次推送都需填写。"
- placement="top-start"
- >
- <span>
- 填写频率
- <i class="el-icon-question" />
- </span>
- </el-tooltip>
- </template>
- <template slot-scope="{ row, $index }">
- <span>{{ row.rate || '/' }}</span>
- <el-button
- v-if="!readonly && !isCul"
- size="mini"
- icon="ibps-icon-edit"
- type="text"
- style="margin-left: 10px"
- @click="openDialog(row, $index)"
- >编辑</el-button
- >
- </template>
- </el-table-column>
- <el-table-column v-if="isCul" label="参数值" prop="value">
- <template slot-scope="{ row }">
- <el-input
- v-if="!readonly"
- v-model="row.value"
- size="mini"
- placeholder="请输入"
- type="number"
- :readonly="readonly"
- />
- <span v-else>{{ row.value || '/' }}</span>
- </template>
- </el-table-column>
- <el-table-column v-if="isCul" label="最终值" prop="result">
- <template slot-scope="{ row }">
- <span>{{ formatResult(row.result) }}</span>
- </template>
- </el-table-column>
- <el-table-column v-if="isCul" label="状态" prop="status">
- <template slot-scope="{ row }">
- <span>{{ row.status || '/' }}</span>
- </template>
- </el-table-column>
- </el-table>
- </el-col>
- </el-row>
- <FacilityDataDialog
- v-if="dialogShow"
- :dialog-visible.sync="dialogShow"
- :params="dialogParams"
- @submit="dialogSubmit"
- />
- </div>
- </template>
- <script>
- import NumberRange from '@/views/component/xcomponent/numberRange.vue'
- import FacilityDataDialog from './facilityDataDialog.vue'
- import { getSetting } from '@/utils/query'
- export default {
- components: {
- NumberRange,
- FacilityDataDialog
- },
- props: {
- formData: {
- type: Object,
- default: () => {}
- },
- readonly: {
- type: Boolean,
- default: false
- },
- isCul: {
- type: Boolean,
- default: true
- }
- },
- data() {
- return {
- dialogParams: {},
- dialogShow: false,
- isFirst: true,
- forms: [],
- multipleSelection: [],
- baoliuxiaoshu: true,
- config: 2,
- isUpdatingFromParent: false // 标记是否来自父组件的更新
- }
- },
- computed: {},
- watch: {
- formData: {
- /*
- handler(val) {
- console.log('formData', val)
- if (val.lieBiaoShuJu) {
- if (!this.isCul) {
- this.forms = this.initData(val.lieBiaoShuJu)
- } else {
- if (this.isFirst) {
- this.forms = this.initData(val.lieBiaoShuJu)
- this.isFirst = false
- }
- }
- }
- this.culXiuZheng()
- },
- */
- handler(val) {
- if (val.lieBiaoShuJu) {
- // 标记开始内部更新,避免无限循环调用initData导致卡死(控制forms里的this.$emit的循环调用)
- this.isUpdatingFromParent = true
- //现在每一次watch到数据变化,都会执行initData方法
- if (!this.isCul) {
- this.forms = this.initData(val.lieBiaoShuJu)
- } else {
- this.forms = this.initData(val.lieBiaoShuJu)
- }
- }
- this.culXiuZheng()
- },
- deep: true,
- immediate: true
- },
- forms: {
- /*
- handler(val) {
- // console.log('forms', val)
- this.culXiuZheng()
- this.$emit('change-data', 'lieBiaoShuJu', JSON.stringify(val))
- },
- */
- handler(val) {
- this.culXiuZheng()
- //只有非内部更新时才向父组件发送数据(避免无限循环的调用导致卡死,由formData里的方法对该变量控制)
- if (!this.isUpdatingFromParent) {
- this.$emit('change-data', 'lieBiaoShuJu', JSON.stringify(val))
- } else {
- this.isUpdatingFromParent = false // 重置标记
- }
- },
- deep: true
- }
- },
- async mounted() {
- const { setting = {} } = this.$store.getters
- const { baoliuxiaoshu = true } = (await getSetting('facilityEnv')) || {}
- this.baoliuxiaoshu = baoliuxiaoshu
- if (
- this.$utils.isNotEmpty(setting.facilityEnv) &&
- this.$utils.isNotEmpty(setting.facilityEnv.precision)
- ) {
- this.config = setting.facilityEnv.precision
- }
- },
- methods: {
- // 弹窗的提交事件
- dialogSubmit(data, index) {
- this.$set(this.forms, index, data)
- // console.log('form', this.forms)
- },
- openDialog(row, $index) {
- this.dialogParams = {
- ...row,
- $index
- }
- this.dialogShow = true
- },
- // 计算时间差
- getDayDifference(date1, date2) {
- // 将两个日期转换为Date对象
- const d1 = new Date(date1)
- const d2 = new Date(date2)
- // 计算两个日期之间的毫秒差
- const timeDiff = d2.getTime() - d1.getTime()
- // 将毫秒差转换为天数并返回
- const dayDiff = Math.ceil(timeDiff / (1000 * 3600 * 24))
- // console.log(dayDiff)
- return dayDiff
- },
- // 初始化数据
- initData(data) {
- data = data.replace(/\n/g, '')
- const ans = JSON.parse(data)
- const result = []
- if (this.isFirst) {
- console.log(ans)
- console.log('formData', this.formData)
- }
- ans.forEach((item) => {
- if (!Object.hasOwn(item, 'label')) {
- item.label = ''
- }
- if (!Object.hasOwn(item, 'unit')) {
- item.unit = '℃'
- }
- if (!Object.hasOwn(item, 'range')) {
- item.range = [null, null]
- }
- if (!Object.hasOwn(item, 'fixValue')) {
- item.fixValue = ''
- }
- if (!Object.hasOwn(item, 'value')) {
- item.value = ''
- }
- if (!Object.hasOwn(item, 'result')) {
- item.result = ''
- }
- if (!Object.hasOwn(item, 'rate')) {
- item.rate = ''
- }
- if (!Object.hasOwn(item, 'status')) {
- item.status = ''
- }
- if (!Object.hasOwn(item, 'period')) {
- item.period = ''
- }
- if (!Object.hasOwn(item, 'date')) {
- item.date = ''
- }
- if (!Object.hasOwn(item, 'startTime')) {
- item.startTime = ''
- }
- // this.formData.jianCeShiJian = '2025-03-01' // 测试用
- if (
- !item.result &&
- item.rate &&
- (this.formData.jianCeShiJian ||
- this.formData.bianZhiShiJian ||
- this.readonly)
- ) {
- let jianCeShiJian = ''
- if (this.readonly || !this.formData.jianCeShiJian) {
- jianCeShiJian = this.formData.bianZhiShiJian?.slice(0, 10)
- } else {
- jianCeShiJian = this.formData.jianCeShiJian?.slice(0, 10)
- }
- const thatDate = new Date(jianCeShiJian)
- let day = thatDate.getDay() // 周几
- if (day === 0) {
- // 周日得到结果为0 需要特判
- day = 7
- }
- const dayofMonth = thatDate.getDate() // 多少号
- const monthofYear = thatDate.getMonth() + 1 // 多少月
- const monthInQuarter = monthofYear % 3 === 0 ? 3 : monthofYear % 3 // 计算当前月在当前季度中的第几个月
- let dayDiff = ''
- console.log(`监测时间:${jianCeShiJian},周${day}`)
- switch (item.period) {
- case 'day':
- if (item.date.indexOf(day) > -1) {
- result.push(item)
- }
- break
- case 'week':
- if (item.date === day) {
- result.push(item)
- }
- break
- case 'month':
- if (item.date === dayofMonth) {
- result.push(item)
- }
- break
- case 'quarter':
- if (item.date === monthInQuarter && dayofMonth === 1) {
- result.push(item)
- }
- break
- case 'repeat':
- dayDiff = this.getDayDifference(item.startTime, jianCeShiJian)
- if (dayDiff >= 0 && dayDiff % (item.date + 1) === 0) {
- result.push(item)
- }
- break
- default:
- break
- }
- } else {
- result.push(item)
- }
- })
- return result
- },
- // 默认单位
- querySearch(queryString, cb) {
- const units = [
- { value: '℃' },
- { value: '%' },
- { value: 'Pa' },
- { value: 'MΩ·CM' },
- { value: 'uS/cm' },
- { value: 'cfu/ml' }
- ]
- // 调用 callback 返回建议列表的数据
- cb(units)
- },
- // 计算状态
- getStatus(range, result) {
- let [min, max] = range
- if (min === null || min === '' || typeof min === 'undefined')
- min = Number.NEGATIVE_INFINITY
- if (max === null || max === '' || typeof max === 'undefined')
- max = Number.POSITIVE_INFINITY
- if (+min === 0 && +max === 0) {
- return '正常'
- }
- if (+result <= +max && +result >= +min) {
- return '正常'
- }
- return '失控'
- },
- // 计算修正值
- culXiuZheng() {
- if (!this.isCul) return
- if (this.readonly) return
- const self = this
- this.forms.forEach((item) => {
- if (item.value) {
- if (!self.baoliuxiaoshu && self.formData.leiXing === '纯水机') {
- //绍兴环境,纯水机不需要保留小数
- if (item.fixValue) {
- item.result = self.addStringDecimals(item.fixValue, item.value)
- } else {
- item.result = +item.value
- }
- } else {
- //其他环境、其他页面
- if (item.fixValue) {
- item.result = (+item.fixValue + +item.value).toFixed(this.config)
- } else {
- item.result = +item.value //无修正值时不处理小数位
- }
- }
- item.status = this.getStatus(item.range, item.result)
- } else {
- item.result = ''
- item.status = ''
- }
- })
- },
- // 展示用格式化:保留两位小数(向下取值),末尾可省略的0不展示
- formatResult(val) {
- if (val === '' || val === null || val === undefined) return '/'
- const num = Number(val)
- if (Number.isNaN(num)) return val
- // 向下取值保留两位小数
- const floored = Math.floor(num * 100) / 100
- let str = String(floored)
- if (str.includes('.')) {
- str = str.replace(/0+$/, '').replace(/\.$/, '')
- }
- return str
- },
- formatData(val) {
- return JSON.stringify(val)
- },
- goAdd() {
- if (this.forms.length >= 10) {
- return this.$message({
- message: '超过最大限制!',
- type: 'warning'
- })
- }
- this.forms.push({
- label: '',
- range: [],
- fixValue: '',
- value: '',
- result: '',
- status: '',
- unit: '',
- period: '',
- date: ''
- })
- },
- addStringDecimals(a, b) {
- // 分割整数和小数部分
- const [int1, dec1 = ''] = a.split('.')
- const [int2, dec2 = ''] = b.split('.')
- // 计算最大小数位数
- const maxDecimal = Math.max(dec1.length, dec2.length)
- // 小数部分补零对齐
- const paddedDec1 = dec1.padEnd(maxDecimal, '0')
- const paddedDec2 = dec2.padEnd(maxDecimal, '0')
- // 拼接完整整数(原值 × 10^maxDecimal)
- const fullInt1 = int1 + paddedDec1
- const fullInt2 = int2 + paddedDec2
- // 大整数相加
- let carry = 0
- let result = ''
- let i = fullInt1.length - 1
- let j = fullInt2.length - 1
- while (i >= 0 || j >= 0 || carry) {
- const digit1 = i >= 0 ? parseInt(fullInt1[i--], 10) : 0
- const digit2 = j >= 0 ? parseInt(fullInt2[j--], 10) : 0
- const sum = digit1 + digit2 + carry
- carry = Math.floor(sum / 10)
- result = (sum % 10) + result
- }
- // 处理前导零
- result = result.replace(/^0+/, '') || '0'
- // 插入小数点
- if (maxDecimal > 0) {
- const pos = result.length - maxDecimal
- if (pos <= 0) {
- // 整数部分为零的情况
- result = '0.' + result.padStart(maxDecimal, '0')
- } else {
- // 正常插入小数点
- result = result.slice(0, pos) + '.' + result.slice(pos)
- }
- }
- return result
- },
- goRemove() {
- this.forms = this.forms.filter(
- (item) => !this.multipleSelection.includes(item)
- )
- },
- handleSelectionChange(val) {
- this.multipleSelection = val
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .ficily-data {
- margin-bottom: 20px;
- .button {
- display: flex;
- flex-direction: row-reverse;
- .el-button {
- margin-left: 5px;
- }
- }
- }
- </style>
|