af-mobile-client-vue3 1.4.15 → 1.4.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/common/otherCharge/ChargePrintSelectorAndRemarks.vue +132 -0
- package/src/components/common/otherCharge/CodePayment.vue +351 -0
- package/src/components/common/otherCharge/FileUploader.vue +599 -0
- package/src/components/common/otherCharge/GridFileUploader.vue +840 -0
- package/src/components/common/otherCharge/PaymentMethodSelector.vue +202 -0
- package/src/components/common/otherCharge/PaymentMethodSelectorCard.vue +45 -0
- package/src/components/common/otherCharge/ReceiptModal.vue +269 -0
- package/src/components/common/otherCharge/index.ts +43 -0
- package/src/components/core/XSelect/index.vue +211 -211
- package/src/components/data/OtherCharge/OtherChargeForm.vue +736 -0
- package/src/components/data/OtherCharge/OtherChargeItem.vue +166 -0
- package/src/components/data/OtherCharge/OtherChargeItemModal.vue +529 -0
package/package.json
CHANGED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import CardContainer from '@af-mobile-client-vue3/components/data/CardContainer/CardContainer.vue'
|
|
3
|
+
import CardHeader from '@af-mobile-client-vue3/components/data/CardContainer/CardHeader.vue'
|
|
4
|
+
import { ref, watch } from 'vue'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
modelValue: string
|
|
8
|
+
remarks: string
|
|
9
|
+
}>()
|
|
10
|
+
|
|
11
|
+
const emit = defineEmits<{
|
|
12
|
+
(e: 'update:modelValue', value: string): void
|
|
13
|
+
(e: 'update:remarks', value: string): void
|
|
14
|
+
}>()
|
|
15
|
+
|
|
16
|
+
const printOptions = [
|
|
17
|
+
{ text: '电子发票', value: '电子发票' },
|
|
18
|
+
{ text: '普通收据', value: '普通收据' },
|
|
19
|
+
{ text: '两者都需要', value: 'both' },
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
const selectedOption = ref(getOptionText(props.modelValue))
|
|
23
|
+
const remarks = ref(props.remarks)
|
|
24
|
+
const showPicker = ref(false)
|
|
25
|
+
|
|
26
|
+
function getOptionText(value: string): string {
|
|
27
|
+
const option = printOptions.find(opt => opt.value === value)
|
|
28
|
+
return option ? option.text : ''
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function onConfirm({ selectedValues }: { selectedValues: string[] }) {
|
|
32
|
+
selectedOption.value = selectedValues[0]
|
|
33
|
+
emit('update:modelValue', selectedValues[0])
|
|
34
|
+
showPicker.value = false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
watch(() => remarks.value, (newValue) => {
|
|
38
|
+
emit('update:remarks', newValue)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// 当从外部更新值时更新内部状态
|
|
42
|
+
watch(() => props.modelValue, (newValue) => {
|
|
43
|
+
selectedOption.value = getOptionText(newValue)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
watch(() => props.remarks, (newValue) => {
|
|
47
|
+
remarks.value = newValue
|
|
48
|
+
})
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<CardContainer class="charge-print-and-remarks">
|
|
53
|
+
<CardHeader title="打印及备注" />
|
|
54
|
+
<div class="charge-print-options">
|
|
55
|
+
<div class="charge-print-options__section">
|
|
56
|
+
<label class="charge-print-options__label">收据打印选项</label>
|
|
57
|
+
<div class="charge-print-options__selector">
|
|
58
|
+
<van-field
|
|
59
|
+
v-model="selectedOption"
|
|
60
|
+
is-link
|
|
61
|
+
readonly
|
|
62
|
+
placeholder="请选择打印方式"
|
|
63
|
+
@click="showPicker = true"
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div class="charge-print-options__section">
|
|
69
|
+
<label class="charge-print-options__label">备注</label>
|
|
70
|
+
<div class="charge-print-options__selector">
|
|
71
|
+
<van-field
|
|
72
|
+
v-model="remarks"
|
|
73
|
+
type="textarea"
|
|
74
|
+
placeholder="可选填写备注信息"
|
|
75
|
+
rows="2"
|
|
76
|
+
autosize
|
|
77
|
+
maxlength="200"
|
|
78
|
+
show-word-limit
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<van-popup v-model:show="showPicker" position="bottom" round teleport="body">
|
|
84
|
+
<van-picker
|
|
85
|
+
:columns="printOptions"
|
|
86
|
+
show-toolbar
|
|
87
|
+
title="选择打印方式"
|
|
88
|
+
@confirm="onConfirm"
|
|
89
|
+
@cancel="showPicker = false"
|
|
90
|
+
/>
|
|
91
|
+
</van-popup>
|
|
92
|
+
</div>
|
|
93
|
+
</CardContainer>
|
|
94
|
+
</template>
|
|
95
|
+
|
|
96
|
+
<style scoped lang="less">
|
|
97
|
+
.charge-print-and-remarks {
|
|
98
|
+
margin-bottom: 16px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.charge-print-options {
|
|
102
|
+
&__section {
|
|
103
|
+
margin-bottom: 16px;
|
|
104
|
+
|
|
105
|
+
&:last-child {
|
|
106
|
+
margin-bottom: 0;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
&__label {
|
|
111
|
+
display: block;
|
|
112
|
+
font-size: 12px;
|
|
113
|
+
font-weight: 500;
|
|
114
|
+
color: #6b7280;
|
|
115
|
+
margin-bottom: 4px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
&__selector {
|
|
119
|
+
position: relative;
|
|
120
|
+
|
|
121
|
+
:deep(.van-field) {
|
|
122
|
+
background-color: #f9fafb;
|
|
123
|
+
border-radius: 6px;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
:deep(.van-field__control) {
|
|
128
|
+
background-color: #f9fafb;
|
|
129
|
+
border-radius: 6px;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
</style>
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { post } from '@af-mobile-client-vue3/services/restTools'
|
|
3
|
+
import useUserStore from '@af-mobile-client-vue3/stores/modules/user'
|
|
4
|
+
import { showDialog, showToast } from 'vant'
|
|
5
|
+
import { defineEmits, defineProps, onMounted, onUnmounted, ref } from 'vue'
|
|
6
|
+
|
|
7
|
+
const props = defineProps({
|
|
8
|
+
show: {
|
|
9
|
+
type: Boolean,
|
|
10
|
+
default: false,
|
|
11
|
+
},
|
|
12
|
+
payment: {
|
|
13
|
+
type: String,
|
|
14
|
+
required: true,
|
|
15
|
+
},
|
|
16
|
+
collection: {
|
|
17
|
+
type: [String, Number],
|
|
18
|
+
required: true,
|
|
19
|
+
},
|
|
20
|
+
type: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: true,
|
|
23
|
+
},
|
|
24
|
+
formData: {
|
|
25
|
+
type: Object,
|
|
26
|
+
required: true,
|
|
27
|
+
},
|
|
28
|
+
user: {
|
|
29
|
+
type: Object,
|
|
30
|
+
default: () => {},
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const emit = defineEmits(['update:show', 'paymentSuccess', 'paymentCancel'])
|
|
35
|
+
|
|
36
|
+
// 状态变量
|
|
37
|
+
const isTimeout = ref(false)
|
|
38
|
+
const queryState = ref(false)
|
|
39
|
+
const showCode = ref(props.show)
|
|
40
|
+
const order = ref({ tradeNo: '', url: '' })
|
|
41
|
+
const timer = ref(null)
|
|
42
|
+
const qrcodeTimer = ref(null)
|
|
43
|
+
const codeAddress = ref('')
|
|
44
|
+
const businessStore = props.user
|
|
45
|
+
const userInfo = ref({ ...businessStore })
|
|
46
|
+
// 当前登录用户
|
|
47
|
+
const currUser = useUserStore().getLogin().f
|
|
48
|
+
console.log(userInfo, currUser)
|
|
49
|
+
|
|
50
|
+
// 创建订单并生成二维码
|
|
51
|
+
async function createOrder(): Promise<void> {
|
|
52
|
+
try {
|
|
53
|
+
// 创建订单
|
|
54
|
+
const orderData = {
|
|
55
|
+
config: userInfo.value.f_orgid === '1767' ? 'xianyangkonggang' : 'xianyang',
|
|
56
|
+
pay_way: props.payment.includes('微信') ? 'weixin' : 'ali',
|
|
57
|
+
money: (Number(props.collection) * 100).toFixed(0),
|
|
58
|
+
attach: {
|
|
59
|
+
f_userfiles_id: userInfo.value.f_userfiles_id,
|
|
60
|
+
f_userinfo_code: userInfo.value.f_userinfo_code,
|
|
61
|
+
f_userinfo_id: userInfo.value.f_userinfo_id,
|
|
62
|
+
f_terminal_num: '',
|
|
63
|
+
type: props.type,
|
|
64
|
+
orgId: currUser.resources.orgid,
|
|
65
|
+
orgName: currUser.resources.orgs,
|
|
66
|
+
depId: currUser.resources.depids,
|
|
67
|
+
depName: currUser.resources.deps,
|
|
68
|
+
operator: currUser.resources.name,
|
|
69
|
+
operatorId: currUser.resources.id,
|
|
70
|
+
f_pregas: 0,
|
|
71
|
+
f_preamount: 0,
|
|
72
|
+
f_totalcost: 0,
|
|
73
|
+
f_collection: 0,
|
|
74
|
+
f_add_gas: '',
|
|
75
|
+
payment: props.payment,
|
|
76
|
+
f_outlets: '手持设备',
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
if (props.type !== '其他收费' && props.type !== '换表' && props.type !== '补卡') {
|
|
80
|
+
if (props.type === '超用收费') {
|
|
81
|
+
orderData.attach.f_add_gas = props.formData.isAccumulative === true ? '计入' : '不计入'
|
|
82
|
+
orderData.attach.f_pregas = props.formData.overusedGas
|
|
83
|
+
orderData.attach.f_preamount = props.formData.overusedAmount
|
|
84
|
+
orderData.attach.f_totalcost = props.formData.receivedAmount
|
|
85
|
+
orderData.attach.f_collection = props.formData.receivedAmount
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
orderData.attach.f_pregas = props.formData.gas
|
|
89
|
+
orderData.attach.f_preamount = props.formData.money
|
|
90
|
+
orderData.attach.f_totalcost = props.formData.totalCost ? props.formData.totalCost : 0
|
|
91
|
+
orderData.attach.f_collection = props.formData.collection
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
orderData.attach.f_preamount = Number(props.collection)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const response = await post('/rs/logic/WeiXinGetCode', { data: orderData })
|
|
99
|
+
console.log('生成订单返回结果', response)
|
|
100
|
+
if (response.code === 200) {
|
|
101
|
+
order.value.tradeNo = response.f_out_trade_no
|
|
102
|
+
order.value.url = response.url
|
|
103
|
+
const url = response.url.replace(/&/g, '|')
|
|
104
|
+
codeAddress.value = `/rs/pay/paintCode?url=${url}`
|
|
105
|
+
console.log(codeAddress.value)
|
|
106
|
+
// 显示二维码
|
|
107
|
+
isTimeout.value = true
|
|
108
|
+
// 开始轮询订单状态
|
|
109
|
+
await startOrderStatusPolling(response.f_out_trade_no)
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
showToast('创建订单失败!')
|
|
113
|
+
emit('paymentCancel')
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.error('创建订单失败', error)
|
|
118
|
+
showToast('创建订单失败,请检查网络!')
|
|
119
|
+
emit('paymentCancel')
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 轮询订单状态
|
|
124
|
+
async function startOrderStatusPolling(tradeNo): Promise<void> {
|
|
125
|
+
let times = 0
|
|
126
|
+
|
|
127
|
+
timer.value = setInterval(async () => {
|
|
128
|
+
times++
|
|
129
|
+
try {
|
|
130
|
+
const orderData = {
|
|
131
|
+
f_out_trade_no: tradeNo,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const result = await post('/af-revenue/logic/mobile_queryThirdOrderState', orderData)
|
|
135
|
+
console.log('==================>', result)
|
|
136
|
+
if (result.length === 0) {
|
|
137
|
+
clearInterval(timer.value)
|
|
138
|
+
showToast({
|
|
139
|
+
message: '未查询到订单信息,请重新点击支付生成订单信息',
|
|
140
|
+
duration: 5000,
|
|
141
|
+
})
|
|
142
|
+
emit('paymentCancel')
|
|
143
|
+
}
|
|
144
|
+
// 支付成功
|
|
145
|
+
if (result[0].f_bill_state === '1') {
|
|
146
|
+
clearInterval(timer.value)
|
|
147
|
+
clearTimeout(qrcodeTimer.value)
|
|
148
|
+
queryState.value = false
|
|
149
|
+
emit('paymentSuccess', { tradeNo, datetime: new Date().toLocaleString() })
|
|
150
|
+
}
|
|
151
|
+
// 支付成功
|
|
152
|
+
if (result[0].f_bill_state === '0') {
|
|
153
|
+
queryState.value = true
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 支付失败
|
|
157
|
+
if (result[0].f_bill_state === '2') {
|
|
158
|
+
clearInterval(timer.value)
|
|
159
|
+
showToast({
|
|
160
|
+
message: '支付失败或支付超时,请重新点击支付生成二维码',
|
|
161
|
+
duration: 5000,
|
|
162
|
+
})
|
|
163
|
+
emit('paymentCancel')
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 查询超时
|
|
167
|
+
if (times > 24) {
|
|
168
|
+
clearInterval(timer.value)
|
|
169
|
+
showToast({
|
|
170
|
+
message: '经多次查询用户支付未成功,请重新点击支付生成二维码',
|
|
171
|
+
duration: 5000,
|
|
172
|
+
})
|
|
173
|
+
emit('paymentCancel')
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
console.error('查询订单状态失败', error)
|
|
178
|
+
}
|
|
179
|
+
}, 5000)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 取消支付
|
|
183
|
+
function cancelPayment() {
|
|
184
|
+
showDialog({
|
|
185
|
+
title: '确认取消',
|
|
186
|
+
message: '确定要取消支付吗?',
|
|
187
|
+
showCancelButton: true,
|
|
188
|
+
}).then((action) => {
|
|
189
|
+
if (action === 'confirm') {
|
|
190
|
+
clearTimers()
|
|
191
|
+
emit('update:show', false)
|
|
192
|
+
emit('paymentCancel')
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 清除所有定时器
|
|
198
|
+
function clearTimers() {
|
|
199
|
+
if (timer.value) {
|
|
200
|
+
clearInterval(timer.value)
|
|
201
|
+
timer.value = null
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (qrcodeTimer.value) {
|
|
205
|
+
clearTimeout(qrcodeTimer.value)
|
|
206
|
+
qrcodeTimer.value = null
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 组件挂载时创建订单
|
|
211
|
+
onMounted(() => {
|
|
212
|
+
if (props.show) {
|
|
213
|
+
createOrder()
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
// 组件卸载时清除定时器
|
|
218
|
+
onUnmounted(() => {
|
|
219
|
+
clearTimers()
|
|
220
|
+
})
|
|
221
|
+
</script>
|
|
222
|
+
|
|
223
|
+
<template>
|
|
224
|
+
<van-popup
|
|
225
|
+
v-model:show="showCode"
|
|
226
|
+
:close-on-click-overlay="false"
|
|
227
|
+
:style="{ width: '90%', maxWidth: '400px' }"
|
|
228
|
+
>
|
|
229
|
+
<div class="qrcode-payment">
|
|
230
|
+
<div class="qrcode-payment__header">
|
|
231
|
+
<div class="qrcode-payment__title">
|
|
232
|
+
<van-icon name="scan" size="24" color="#1989fa" />
|
|
233
|
+
<span>请扫描下方二维码进行付款</span>
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
<div class="qrcode-payment__amount">
|
|
237
|
+
<span class="qrcode-payment__label">支付金额:</span>
|
|
238
|
+
<span class="qrcode-payment__value">¥{{ props.collection }}元</span>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<div class="qrcode-payment__content">
|
|
243
|
+
<div v-if="queryState" class="qrcode-payment__query-tip">
|
|
244
|
+
用户付款过程中,请勿退出或取消!
|
|
245
|
+
</div>
|
|
246
|
+
<div class="qrcode-payment__qrcode-container">
|
|
247
|
+
<img
|
|
248
|
+
v-if="isTimeout"
|
|
249
|
+
:src="codeAddress"
|
|
250
|
+
class="qrcode-payment__timeout-img"
|
|
251
|
+
alt=""
|
|
252
|
+
>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
<div class="qrcode-payment__footer">
|
|
257
|
+
<van-button
|
|
258
|
+
type="default"
|
|
259
|
+
block
|
|
260
|
+
@click="cancelPayment"
|
|
261
|
+
>
|
|
262
|
+
取消支付
|
|
263
|
+
</van-button>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
</van-popup>
|
|
267
|
+
</template>
|
|
268
|
+
|
|
269
|
+
<style lang="less" scoped>
|
|
270
|
+
.qrcode-payment {
|
|
271
|
+
padding: 20px;
|
|
272
|
+
|
|
273
|
+
&__query-tip {
|
|
274
|
+
text-align: center;
|
|
275
|
+
color: #ff4d4f;
|
|
276
|
+
font-size: 14px;
|
|
277
|
+
font-weight: bold;
|
|
278
|
+
background-color: #fff;
|
|
279
|
+
padding: 4px 8px;
|
|
280
|
+
margin-bottom: 12px;
|
|
281
|
+
width: 266px;
|
|
282
|
+
word-wrap: break-word;
|
|
283
|
+
white-space: pre-wrap;
|
|
284
|
+
line-height: 1.4;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
&__header {
|
|
288
|
+
margin-bottom: 20px;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
&__title {
|
|
292
|
+
display: flex;
|
|
293
|
+
align-items: center;
|
|
294
|
+
justify-content: center;
|
|
295
|
+
gap: 8px;
|
|
296
|
+
margin-bottom: 16px;
|
|
297
|
+
font-size: 16px;
|
|
298
|
+
font-weight: 500;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
&__order-info,
|
|
302
|
+
&__amount {
|
|
303
|
+
display: flex;
|
|
304
|
+
justify-content: center;
|
|
305
|
+
margin-bottom: 8px;
|
|
306
|
+
font-size: 14px;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
&__label {
|
|
310
|
+
color: #646566;
|
|
311
|
+
margin-right: 4px;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
&__value {
|
|
315
|
+
color: #323233;
|
|
316
|
+
font-weight: 500;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
&__content {
|
|
320
|
+
display: flex;
|
|
321
|
+
flex-direction: column;
|
|
322
|
+
align-items: center;
|
|
323
|
+
justify-content: center;
|
|
324
|
+
margin-bottom: 20px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
&__qrcode-container {
|
|
328
|
+
position: relative;
|
|
329
|
+
width: 266px;
|
|
330
|
+
height: 266px;
|
|
331
|
+
background-color: #f7f8fa;
|
|
332
|
+
display: flex;
|
|
333
|
+
justify-content: center;
|
|
334
|
+
align-items: center;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
&__timeout-img {
|
|
338
|
+
width: 266px;
|
|
339
|
+
height: 266px;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
&__qrcode {
|
|
343
|
+
width: 266px;
|
|
344
|
+
height: 266px;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
&__footer {
|
|
348
|
+
margin-top: 16px;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
</style>
|