|
|
@@ -0,0 +1,389 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container tax-page">
|
|
|
+ <el-alert
|
|
|
+ v-if="!bindInfo.deviceSn"
|
|
|
+ title="当前用户未绑定调试宝设备,无法同步页面数据"
|
|
|
+ type="warning"
|
|
|
+ show-icon
|
|
|
+ :closable="false"
|
|
|
+ class="mb16"
|
|
|
+ />
|
|
|
+ <el-row v-else type="flex" justify="space-between" align="middle" class="mb16">
|
|
|
+ <el-col :span="16">
|
|
|
+ <el-tag :type="wsConnected ? 'success' : 'info'" size="small">
|
|
|
+ {{ wsConnected ? 'WebSocket 已连接' : 'WebSocket 未连接' }}
|
|
|
+ </el-tag>
|
|
|
+ <span class="bind-text">设备 SN:{{ bindInfo.deviceSn }}</span>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8" style="text-align: right">
|
|
|
+ <el-button size="mini" @click="loadBind">刷新绑定</el-button>
|
|
|
+ <el-button size="mini" type="primary" @click="reconnectWs">重连</el-button>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 顶部参数行 -->
|
|
|
+ <el-card shadow="never" class="mb16">
|
|
|
+ <el-row :gutter="12" class="param-row">
|
|
|
+ <el-col :xs="12" :sm="6" :md="4">
|
|
|
+ <div class="field-label">接口号</div>
|
|
|
+ <el-select v-model="form.interfaceNo" size="small" @change="onInterfaceNoChange">
|
|
|
+ <el-option label="A" value="A" />
|
|
|
+ <el-option label="B" value="B" />
|
|
|
+ </el-select>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="12" :sm="6" :md="4">
|
|
|
+ <div class="field-label">枪号</div>
|
|
|
+ <el-input-number v-model="form.gunNo" :min="1" :max="9" size="small" controls-position="right" @change="onGunNoChange" />
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="12" :sm="6" :md="4">
|
|
|
+ <div class="field-label">新国标</div>
|
|
|
+ <el-switch v-model="newStandardOn" active-text="启用" inactive-text="禁用" @change="onNewStandardChange" />
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="12" :sm="6" :md="4">
|
|
|
+ <div class="field-label">新国标报税口</div>
|
|
|
+ <el-input-number v-model="form.newNationalStandardTaxNo" :min="1" :max="9" size="small" controls-position="right" @change="onNewNationalStandardTaxNoChange" />
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <!-- 左侧:税控序列号 -->
|
|
|
+ <el-col :xs="24" :md="10" :lg="8">
|
|
|
+ <el-card shadow="hover" class="panel-card">
|
|
|
+ <div slot="header" class="panel-title">税控序列号查询</div>
|
|
|
+ <el-button type="primary" size="small" class="mb16" @click="queryTaxSerial">查询税控序列号</el-button>
|
|
|
+ <div class="result-item"><span class="label">税控序列号:</span>{{ form.taxNo || '-' }}</div>
|
|
|
+ <div class="result-item"><span class="label">厂家:</span>{{ form.manufacturer || '-' }}</div>
|
|
|
+ <div class="result-item"><span class="label">枪个数:</span>{{ form.gunNumber != null ? form.gunNumber : '-' }}</div>
|
|
|
+ <div class="result-item"><span class="label">执行结果:</span>
|
|
|
+ <el-tag size="mini" :type="resultTag(form.queryTaxResult)">{{ form.queryTaxResult || '-' }}</el-tag>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <!-- 右侧:累计数据 -->
|
|
|
+ <el-col :xs="24" :md="14" :lg="16">
|
|
|
+ <el-card shadow="hover" class="panel-card">
|
|
|
+ <div slot="header" class="panel-title">累计数据查询</div>
|
|
|
+ <el-row :gutter="8" class="mb16 date-row">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-input-number v-model="queryYear" :min="2000" :max="2099" size="small" controls-position="right" @change="onQueryDateChange" />
|
|
|
+ <span class="unit">年</span>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-input-number v-model="queryMonth" :min="1" :max="12" size="small" controls-position="right" @change="onQueryDateChange" />
|
|
|
+ <span class="unit">月</span>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-input-number v-model="queryDay" :min="1" :max="31" size="small" controls-position="right" @change="onQueryDateChange" />
|
|
|
+ <span class="unit">日</span>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-button-group class="mb16 btn-group-wrap">
|
|
|
+ <el-button type="primary" size="small" @click="queryAccumulated(BUTTON_TYPE.QUERY_CURRENT)">查当次及总累</el-button>
|
|
|
+ <el-button type="primary" size="small" @click="queryAccumulated(BUTTON_TYPE.QUERY_DAY)">查日累</el-button>
|
|
|
+ <el-button type="primary" size="small" @click="queryAccumulated(BUTTON_TYPE.QUERY_MONTH)">查月累</el-button>
|
|
|
+ </el-button-group>
|
|
|
+ <el-row :gutter="12">
|
|
|
+ <el-col :xs="12" :sm="8"><div class="result-item"><span class="label">金额:</span>{{ formatNum(form.amount) }}</div></el-col>
|
|
|
+ <el-col :xs="12" :sm="8"><div class="result-item"><span class="label">油量:</span>{{ formatNum(form.volume) }}</div></el-col>
|
|
|
+ <el-col :xs="12" :sm="8"><div class="result-item"><span class="label">单价:</span>{{ formatNum(form.unitPrice) }}</div></el-col>
|
|
|
+ <el-col :xs="12" :sm="8"><div class="result-item"><span class="label">是否密文:</span>{{ encryptionText }}</div></el-col>
|
|
|
+ <el-col :xs="12" :sm="8"><div class="result-item"><span class="label">总金额:</span>{{ formatNum(form.totalAmount) }}</div></el-col>
|
|
|
+ <el-col :xs="12" :sm="8"><div class="result-item"><span class="label">总油量:</span>{{ formatNum(form.totalVolume) }}</div></el-col>
|
|
|
+ <el-col :span="24"><div class="result-item"><span class="label">执行结果:</span>
|
|
|
+ <el-tag size="mini" :type="resultTag(form.queryAccumulatedResult)">{{ form.queryAccumulatedResult || '-' }}</el-tag>
|
|
|
+ </div></el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { getTsbWsBind } from '@/api/tsb/ws'
|
|
|
+import tsbWebSocket from '@/utils/tsbWebSocket'
|
|
|
+import { getRouteByCmdType } from '@/utils/tsbCmdRoute'
|
|
|
+
|
|
|
+const PAGE_CMD = 'common:tax'
|
|
|
+const PAGE_ROUTE = getRouteByCmdType(PAGE_CMD)
|
|
|
+
|
|
|
+/** 按钮操作类型 */
|
|
|
+const BUTTON_TYPE = {
|
|
|
+ QUERY: 'common:tax:query',
|
|
|
+ QUERY_CURRENT: 'common:tax:queryCurrent',
|
|
|
+ QUERY_DAY: 'common:tax:queryDay',
|
|
|
+ QUERY_MONTH: 'common:tax:queryMonth'
|
|
|
+}
|
|
|
+
|
|
|
+/** 报税口页面展示字段(与接口文档 data 映射一致) */
|
|
|
+const TAX_DISPLAY_FIELDS = [
|
|
|
+ 'interfaceNo',
|
|
|
+ 'gunNo',
|
|
|
+ 'newNationalStandard',
|
|
|
+ 'newNationalStandardTaxNo',
|
|
|
+ 'queryDate',
|
|
|
+ 'taxNo',
|
|
|
+ 'manufacturer',
|
|
|
+ 'gunNumber',
|
|
|
+ 'queryTaxResult',
|
|
|
+ 'amount',
|
|
|
+ 'volume',
|
|
|
+ 'unitPrice',
|
|
|
+ 'encryption',
|
|
|
+ 'totalAmount',
|
|
|
+ 'totalVolume',
|
|
|
+ 'queryAccumulatedResult'
|
|
|
+]
|
|
|
+
|
|
|
+function createDefaultForm() {
|
|
|
+ const now = new Date()
|
|
|
+ const m = String(now.getMonth() + 1).padStart(2, '0')
|
|
|
+ const d = String(now.getDate()).padStart(2, '0')
|
|
|
+ return {
|
|
|
+ interfaceNo: 'A',
|
|
|
+ gunNo: 1,
|
|
|
+ newNationalStandard: 0,
|
|
|
+ newNationalStandardTaxNo: 1,
|
|
|
+ queryDate: `${now.getFullYear()}-${m}-${d}`,
|
|
|
+ taxNo: '',
|
|
|
+ manufacturer: '',
|
|
|
+ gunNumber: null,
|
|
|
+ queryTaxResult: '',
|
|
|
+ amount: null,
|
|
|
+ volume: null,
|
|
|
+ unitPrice: null,
|
|
|
+ encryption: null,
|
|
|
+ totalAmount: null,
|
|
|
+ totalVolume: null,
|
|
|
+ queryAccumulatedResult: ''
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function createDefaultDateParts() {
|
|
|
+ const now = new Date()
|
|
|
+ return {
|
|
|
+ queryYear: now.getFullYear(),
|
|
|
+ queryMonth: now.getMonth() + 1,
|
|
|
+ queryDay: now.getDate()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'TsbTaxPage',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ BUTTON_TYPE,
|
|
|
+ bindInfo: {},
|
|
|
+ wsConnected: false,
|
|
|
+ newStandardOn: false,
|
|
|
+ ...createDefaultDateParts(),
|
|
|
+ form: createDefaultForm(),
|
|
|
+ wsTimer: null
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ encryptionText() {
|
|
|
+ if (this.form.encryption === 1) return '是'
|
|
|
+ if (this.form.encryption === 0) return '否'
|
|
|
+ return '-'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ '$store.state.tsb.pageDataVersion'() {
|
|
|
+ this.tryApplyWsPageData(false)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.loadBind()
|
|
|
+ tsbWebSocket.connect()
|
|
|
+ this._enteredByCreated = true
|
|
|
+ this.initPageData()
|
|
|
+ this.wsTimer = setInterval(() => {
|
|
|
+ this.wsConnected = tsbWebSocket.isConnected()
|
|
|
+ }, 1000)
|
|
|
+ },
|
|
|
+ activated() {
|
|
|
+ if (this.tryApplyWsPageData(true)) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (this._enteredByCreated) {
|
|
|
+ this._enteredByCreated = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.applyManualDefaults()
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ if (this.wsTimer) clearInterval(this.wsTimer)
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ loadBind() {
|
|
|
+ getTsbWsBind().then(res => {
|
|
|
+ this.bindInfo = res.data || {}
|
|
|
+ }).catch(() => {
|
|
|
+ this.bindInfo = {}
|
|
|
+ })
|
|
|
+ },
|
|
|
+ reconnectWs() {
|
|
|
+ tsbWebSocket.reconnect()
|
|
|
+ },
|
|
|
+ /** WebSocket 推送进入:用 data 初始化;手动菜单进入:恢复默认值并下发 */
|
|
|
+ initPageData() {
|
|
|
+ if (this.tryApplyWsPageData(true)) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.applyManualDefaults()
|
|
|
+ },
|
|
|
+ /** 手动进入:重置为默认值并同步到设备 */
|
|
|
+ applyManualDefaults() {
|
|
|
+ this.resetFormDefaults()
|
|
|
+ this.syncDefaultParamsToDevice()
|
|
|
+ },
|
|
|
+ /** 从 store 应用 WebSocket 推送数据 */
|
|
|
+ tryApplyWsPageData(clearInitFlag) {
|
|
|
+ const data = this.$store.state.tsb.pageData[PAGE_CMD]
|
|
|
+ const initFromWs = this.$store.state.tsb.initFromWs[PAGE_CMD]
|
|
|
+ if (!data) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if (initFromWs) {
|
|
|
+ this.applyTaxData(data)
|
|
|
+ if (clearInitFlag) {
|
|
|
+ this.$store.commit('tsb/CLEAR_WS_INIT', PAGE_CMD)
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ if (this.$route.path === PAGE_ROUTE) {
|
|
|
+ this.applyTaxData(data)
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ },
|
|
|
+ resetFormDefaults() {
|
|
|
+ const dateParts = createDefaultDateParts()
|
|
|
+ this.queryYear = dateParts.queryYear
|
|
|
+ this.queryMonth = dateParts.queryMonth
|
|
|
+ this.queryDay = dateParts.queryDay
|
|
|
+ this.form = createDefaultForm()
|
|
|
+ this.newStandardOn = false
|
|
|
+ },
|
|
|
+ applyTaxData(data) {
|
|
|
+ if (!data) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const defaults = createDefaultForm()
|
|
|
+ TAX_DISPLAY_FIELDS.forEach((key) => {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
|
+ this.form[key] = data[key] ?? defaults[key]
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (Object.prototype.hasOwnProperty.call(data, 'newNationalStandard')) {
|
|
|
+ this.newStandardOn = data.newNationalStandard === 1
|
|
|
+ }
|
|
|
+ if (Object.prototype.hasOwnProperty.call(data, 'queryDate')) {
|
|
|
+ this.parseQueryDate(data.queryDate)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ parseQueryDate(dateStr) {
|
|
|
+ if (dateStr == null || dateStr === '') {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const parts = dateStr.split('-')
|
|
|
+ if (parts.length === 3) {
|
|
|
+ this.queryYear = Number(parts[0])
|
|
|
+ this.queryMonth = Number(parts[1])
|
|
|
+ this.queryDay = Number(parts[2])
|
|
|
+ }
|
|
|
+ },
|
|
|
+ updateQueryDate() {
|
|
|
+ const m = String(this.queryMonth).padStart(2, '0')
|
|
|
+ const d = String(this.queryDay).padStart(2, '0')
|
|
|
+ this.form.queryDate = `${this.queryYear}-${m}-${d}`
|
|
|
+ },
|
|
|
+ /** 手动进入时下发默认参数区字段 */
|
|
|
+ syncDefaultParamsToDevice() {
|
|
|
+ this.updateQueryDate()
|
|
|
+ const payload = {
|
|
|
+ interfaceNo: this.form.interfaceNo,
|
|
|
+ gunNo: this.form.gunNo,
|
|
|
+ newNationalStandard: this.form.newNationalStandard,
|
|
|
+ newNationalStandardTaxNo: this.form.newNationalStandardTaxNo,
|
|
|
+ queryDate: this.form.queryDate
|
|
|
+ }
|
|
|
+ const send = () => tsbWebSocket.sendPageSync(PAGE_CMD, payload)
|
|
|
+ if (send()) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ let retries = 0
|
|
|
+ const timer = setInterval(() => {
|
|
|
+ if (send() || ++retries >= 10) {
|
|
|
+ clearInterval(timer)
|
|
|
+ }
|
|
|
+ }, 500)
|
|
|
+ },
|
|
|
+ /** 字段变更:仅下发变更字段 */
|
|
|
+ syncField(payload) {
|
|
|
+ tsbWebSocket.sendPageSync(PAGE_CMD, payload)
|
|
|
+ },
|
|
|
+ onInterfaceNoChange(val) {
|
|
|
+ this.syncField({ interfaceNo: val })
|
|
|
+ },
|
|
|
+ onGunNoChange(val) {
|
|
|
+ this.syncField({ gunNo: val })
|
|
|
+ },
|
|
|
+ onNewStandardChange(val) {
|
|
|
+ this.form.newNationalStandard = val ? 1 : 0
|
|
|
+ this.syncField({ newNationalStandard: this.form.newNationalStandard })
|
|
|
+ },
|
|
|
+ onNewNationalStandardTaxNoChange(val) {
|
|
|
+ this.syncField({ newNationalStandardTaxNo: val })
|
|
|
+ },
|
|
|
+ onQueryDateChange() {
|
|
|
+ this.updateQueryDate()
|
|
|
+ this.syncField({ queryDate: this.form.queryDate })
|
|
|
+ },
|
|
|
+ /** 按钮点击:下发参数区字段 + buttonType */
|
|
|
+ buildButtonPayload(buttonType) {
|
|
|
+ this.updateQueryDate()
|
|
|
+ return {
|
|
|
+ interfaceNo: this.form.interfaceNo,
|
|
|
+ gunNo: this.form.gunNo,
|
|
|
+ newNationalStandard: this.form.newNationalStandard,
|
|
|
+ newNationalStandardTaxNo: this.form.newNationalStandardTaxNo,
|
|
|
+ queryDate: this.form.queryDate,
|
|
|
+ buttonType
|
|
|
+ }
|
|
|
+ },
|
|
|
+ sendButtonAction(buttonType) {
|
|
|
+ tsbWebSocket.sendPageSync(PAGE_CMD, this.buildButtonPayload(buttonType))
|
|
|
+ },
|
|
|
+ queryTaxSerial() {
|
|
|
+ this.sendButtonAction(BUTTON_TYPE.QUERY)
|
|
|
+ },
|
|
|
+ queryAccumulated(buttonType) {
|
|
|
+ this.sendButtonAction(buttonType)
|
|
|
+ },
|
|
|
+ formatNum(v) {
|
|
|
+ return v == null || v === '' ? '-' : v
|
|
|
+ },
|
|
|
+ resultTag(text) {
|
|
|
+ if (!text) return 'info'
|
|
|
+ return text.indexOf('成功') >= 0 ? 'success' : 'danger'
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.tax-page .mb16 { margin-bottom: 16px; }
|
|
|
+.field-label { font-size: 13px; color: #606266; margin-bottom: 6px; }
|
|
|
+.bind-text { margin-left: 12px; color: #909399; font-size: 13px; }
|
|
|
+.panel-card { min-height: 280px; }
|
|
|
+.panel-title { font-weight: 600; }
|
|
|
+.result-item { margin-bottom: 10px; font-size: 14px; line-height: 1.6; }
|
|
|
+.result-item .label { color: #909399; }
|
|
|
+.date-row .unit { margin-left: 4px; font-size: 13px; color: #606266; }
|
|
|
+.btn-group-wrap { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
|
+.param-row >>> .el-select, .param-row >>> .el-input-number { width: 100%; }
|
|
|
+</style>
|