Commit f566491d5b27e0f5446937663c279ab6a5e8e107

Authored by 李宇
1 parent 9b4afe08

最新

绿纤uni-app/pages/addServiceLog/addServiceLogcopy.vue 0 → 100644
  1 +<template>
  2 + <view class="container">
  3 + <!-- 会员信息卡片 -->
  4 + <view class="member-card">
  5 + <view class="member-info">
  6 + <view class="member-avatar">👤</view>
  7 + <view class="member-details">
  8 + <view class="member-name">{{ memberName }}</view>
  9 + <view class="member-id">消费记录ID: {{ consumeId }}</view>
  10 + </view>
  11 + </view>
  12 + </view>
  13 +
  14 + <!-- 表单卡片 -->
  15 + <view class="form-card">
  16 + <view class="form-content">
  17 + <form @submit="handleFormSubmit">
  18 + <!-- 使用前图片 -->
  19 + <view class="form-group">
  20 + <text class="form-label">使用前图片</text>
  21 + <view class="input-wrapper">
  22 + <view class="file-upload-area" @tap.stop="chooseFile('before')">
  23 + <view class="file-upload-text">
  24 + <text class="file-upload-icon">📷</text>
  25 + <text>点击选择使用前图片</text>
  26 + </view>
  27 + <view class="file-list">
  28 + <view v-for="(file, index) in beforeImages" :key="index" class="file-item">
  29 + <view class="file-info">
  30 + <text class="file-icon">🖼️</text>
  31 + <text class="file-name">{{ file.name }}</text>
  32 + </view>
  33 + <view class="file-actions">
  34 + <view v-if="file.status === 'uploading'" class="file-status uploading">上传中...</view>
  35 + <view v-else-if="file.status === 'success'" class="file-status success">✓ 已上传</view>
  36 + <view v-else-if="file.status === 'error'" class="file-status error">✗ 上传失败</view>
  37 + <button v-if="file.status === 'success'" class="btn-error" @click.stop="removeFile('before', index)">删除</button>
  38 + </view>
  39 + </view>
  40 + </view>
  41 + </view>
  42 + </view>
  43 + </view>
  44 +
  45 + <!-- 使用后图片 -->
  46 + <view class="form-group">
  47 + <text class="form-label">使用后图片</text>
  48 + <view class="input-wrapper">
  49 + <view class="file-upload-area" @tap.stop="chooseFile('after')">
  50 + <view class="file-upload-text">
  51 + <text class="file-upload-icon">📷</text>
  52 + <text>点击选择使用后图片</text>
  53 + </view>
  54 + <view class="file-list">
  55 + <view v-for="(file, index) in afterImages" :key="index" class="file-item">
  56 + <view class="file-info">
  57 + <text class="file-icon">🖼️</text>
  58 + <text class="file-name">{{ file.name }}</text>
  59 + </view>
  60 + <view class="file-actions">
  61 + <view v-if="file.status === 'uploading'" class="file-status uploading">上传中...</view>
  62 + <view v-else-if="file.status === 'success'" class="file-status success">✓ 已上传</view>
  63 + <view v-else-if="file.status === 'error'" class="file-status error">✗ 上传失败</view>
  64 + <button v-if="file.status === 'success'" class="btn-error" @click.stop="removeFile('after', index)">删除</button>
  65 + </view>
  66 + </view>
  67 + </view>
  68 + </view>
  69 + </view>
  70 + </view>
  71 + <!-- 语音备注(H5 环境自动隐藏,仅小程序 / App 生效) -->
  72 + <view class="form-group" v-if="enableVoice">
  73 + <text class="form-label">语音备注</text>
  74 + <view class="voice-card">
  75 + <view class="voice-main">
  76 + <view class="voice-record-btn" :class="{ recording: voiceNote.status === 'recording' }" @tap="toggleRecord">
  77 + <!-- <text class="voice-record-icon">🎙️</text> -->
  78 + <u-icon name="mic" color="#fff" size="28"></u-icon>
  79 + </view>
  80 + <view class="voice-info">
  81 + <view class="voice-status-text">
  82 + {{ voiceStatusText }}
  83 + </view>
  84 + <view class="voice-duration" v-if="voiceNote.duration">
  85 + {{ formatVoiceDuration(voiceNote.duration) }}
  86 + </view>
  87 + </view>
  88 + </view>
  89 + <view class="voice-actions" v-if="voiceNote.url">
  90 + <view class="voice-wave" :class="{ playing: voiceNote.isPlaying }">
  91 + <view class="wave-bar" v-for="n in 5" :key="n"></view>
  92 + </view>
  93 + <button class="btn-plain" @tap.stop="togglePlayVoice">
  94 + {{ voiceNote.isPlaying ? '暂停播放' : '播放语音' }}
  95 + </button>
  96 + <button class="btn-error" @tap.stop="removeVoice">删除</button>
  97 + </view>
  98 + </view>
  99 + </view>
  100 +
  101 + <view class="form-group" v-else>
  102 + <text class="form-label">语音备注</text>
  103 + <view class="voice-card voice-card--disabled">
  104 + <view class="voice-main">
  105 + <view class="voice-record-btn">
  106 + <text class="voice-record-icon">🎙️</text>
  107 + </view>
  108 + <view class="voice-info">
  109 + <view class="voice-status-text">
  110 + 当前 H5 环境暂不支持录音,请在小程序或 App 中使用语音备注。
  111 + </view>
  112 + </view>
  113 + </view>
  114 + </view>
  115 + </view>
  116 + <!-- 反馈备注 -->
  117 + <view class="form-group">
  118 + <text class="form-label">反馈备注</text>
  119 + <view class="input-wrapper">
  120 + <textarea v-model="formData.remark" placeholder="请输入反馈备注" :maxlength="500" :count="true"
  121 + class="textarea-input" />
  122 + </view>
  123 + </view>
  124 + <!-- 科技部备注 -->
  125 + <view class="form-group">
  126 + <text class="form-label">科技部备注</text>
  127 + <view class="input-wrapper">
  128 + <textarea v-model="formData.kjbRemark" placeholder="请输入科技部备注" :maxlength="500" :count="true"
  129 + class="textarea-input" />
  130 + </view>
  131 + </view>
  132 +
  133 + <!-- 提交按钮 -->
  134 + <view class="btn-group">
  135 + <button type="submit" class="btn btn-primary" :style="{opacity: isSubmitting?0.5:1}" @tap="isSubmitting?null:submitServiceLog()">
  136 + {{ isSubmitting?(isEditMode?'更新中...':'提交中...'):(isEditMode?'更新服务日志':'提交服务日志') }}
  137 + </button>
  138 + </view>
  139 + </form>
  140 + </view>
  141 + </view>
  142 + </view>
  143 +</template>
  144 +
  145 +<script>
  146 + import lxApi from '@/apis/modules/lx.js'
  147 + import config from '@/common/config.js'
  148 +
  149 + export default {
  150 + data() {
  151 + return {
  152 + consumeId: '',
  153 + memberName: '',
  154 + logId: '',
  155 + isEditMode: false,
  156 + isSubmitting: false,
  157 + baseUrl: config.getImgBaseUrl(),
  158 + formData: {
  159 + remark: '',
  160 + kjbRemark: ''
  161 + },
  162 + beforeImages: [],
  163 + afterImages: [],
  164 + enableVoice: true,
  165 + voiceNote: {
  166 + fileId: '',
  167 + url: '',
  168 + duration: 0,
  169 + status: 'idle', // idle | recording | recorded | processing
  170 + uploading: false,
  171 + isPlaying: false
  172 + },
  173 + recorderInited: false
  174 + }
  175 + },
  176 + onReady() {
  177 + // #ifdef H5
  178 + this.enableVoice = false
  179 + // #endif
  180 + // #ifndef H5
  181 + this.initRecorder()
  182 + // #endif
  183 + },
  184 + onLoad(options) {
  185 + // 获取传递的参数
  186 + this.consumeId = options.consumeId || ''
  187 + this.memberName = decodeURIComponent(options.memberName || '未知会员')
  188 + this.logId = options.logId || ''
  189 + this.isEditMode = options.mode === 'edit'
  190 +
  191 + // 如果是编辑模式,加载现有数据
  192 + if (this.isEditMode && this.logId) {
  193 + this.loadServiceLogDetail()
  194 + }
  195 + },
  196 + computed: {
  197 + voiceStatusText() {
  198 + if (this.voiceNote.status === 'recording') {
  199 + return '正在录音,再次点击即可结束'
  200 + }
  201 + if (this.voiceNote.status === 'processing' || this.voiceNote.uploading) {
  202 + return '语音处理中 / 上传中...'
  203 + }
  204 + if (this.voiceNote.url) {
  205 + return '已录制语音,可点击播放或重新录制'
  206 + }
  207 + return '点击麦克风图标开始录音'
  208 + }
  209 + },
  210 + methods: {
  211 + // 返回上一页
  212 + goBack() {
  213 + uni.navigateBack({
  214 + delta: 1
  215 + })
  216 + },
  217 +
  218 + // 加载服务日志详情(编辑模式)
  219 + async loadServiceLogDetail() {
  220 + try {
  221 + uni.showLoading({
  222 + title: '加载中...'
  223 + })
  224 +
  225 + const result = await lxApi.getServiceLogDetail(this.logId)
  226 + uni.hideLoading()
  227 +
  228 + if (result.code === 200 && result.data) {
  229 + const log = result.data
  230 +
  231 + // 填充表单数据
  232 + this.formData.remark = log.remark || ''
  233 + this.formData.kjbRemark = log.kjbRemark || ''
  234 +
  235 + // 加载图片数据
  236 + this.beforeImages = this.parseImageJson(log.beforeImage)
  237 + this.afterImages = this.parseImageJson(log.afterImage)
  238 +
  239 + // 加载语音备注数据(如果后端已支持)
  240 + if (log.voiceUrl) {
  241 + this.voiceNote.url = log.voiceUrl
  242 + this.voiceNote.duration = log.voiceDuration || 0
  243 + this.voiceNote.fileId = log.voiceFileId || ''
  244 + this.voiceNote.status = 'recorded'
  245 + }
  246 + } else {
  247 + uni.showToast({
  248 + title: result.msg || '加载失败',
  249 + icon: 'none'
  250 + })
  251 + }
  252 + } catch (error) {
  253 + uni.hideLoading()
  254 + console.error('加载服务日志详情失败:', error)
  255 + uni.showToast({
  256 + title: '加载失败,请稍后重试',
  257 + icon: 'none'
  258 + })
  259 + }
  260 + },
  261 +
  262 + // 解析图片JSON字符串
  263 + parseImageJson(imageJson) {
  264 + try {
  265 + if (!imageJson) return []
  266 + const images = JSON.parse(imageJson)
  267 + // 将现有图片转换为文件列表格式
  268 + return images.map((image, index) => ({
  269 + id: `existing_${Date.now()}_${index}`,
  270 + name: image.name || image.fileId || `图片${index + 1}`,
  271 + size: 0,
  272 + type: 'image/jpeg',
  273 + status: 'success',
  274 + url: image.url || '',
  275 + fileId: image.fileId || image.name || ''
  276 + }))
  277 + } catch (error) {
  278 + console.error('解析图片JSON失败:', error)
  279 + return []
  280 + }
  281 + },
  282 +
  283 + // 选择文件
  284 + chooseFile(type) {
  285 + uni.chooseImage({
  286 + count: 9,
  287 + success: (res) => {
  288 + // 将tempFilePaths转换为tempFiles格式
  289 + const tempFiles = res.tempFilePaths.map((path, index) => ({
  290 + path: path,
  291 + tempFilePath: path,
  292 + size: res.tempFiles ? res.tempFiles[index].size : 0,
  293 + name: `image_${Date.now()}_${index}.jpg`,
  294 + type: 'image/jpeg'
  295 + }));
  296 + this.handleFileSelect(tempFiles, type);
  297 + }
  298 + });
  299 + },
  300 +
  301 + // 处理文件选择
  302 + async handleFileSelect(files, type) {
  303 + for (let i = 0; i < files.length; i++) {
  304 + const file = files[i];
  305 +
  306 + // 验证文件大小 (限制为10MB)
  307 + if (file.size > 10 * 1024 * 1024) {
  308 + uni.showToast({
  309 + title: `文件 ${file.name} 大小超过10MB限制`,
  310 + icon: 'none'
  311 + });
  312 + continue;
  313 + }
  314 +
  315 + // 上传文件
  316 + await this.uploadSingleFile(file, type);
  317 + }
  318 + },
  319 +
  320 + // 上传单个文件
  321 + async uploadSingleFile(file, type) {
  322 + const fileId = `file_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  323 +
  324 + // 添加到文件列表显示
  325 + this.addFileToList(file, fileId, 'uploading', type);
  326 +
  327 + try {
  328 + const result = await lxApi.uploadFile(file);
  329 +
  330 + if (result.code === 200 && result.data) {
  331 + // 更新文件状态为成功
  332 + this.updateFileStatus(fileId, 'success', type, result.data);
  333 +
  334 + uni.showToast({
  335 + title: '文件上传成功',
  336 + icon: 'success'
  337 + });
  338 + } else {
  339 + throw new Error(result.msg || '上传失败');
  340 + }
  341 + } catch (error) {
  342 + console.error('文件上传失败:', error);
  343 + this.updateFileStatus(fileId, 'error', type);
  344 + uni.showToast({
  345 + title: `文件 ${file.name} 上传失败`,
  346 + icon: 'none'
  347 + });
  348 + }
  349 + },
  350 +
  351 + // 添加文件到列表显示
  352 + addFileToList(file, fileId, status, type) {
  353 + const fileItem = {
  354 + id: fileId,
  355 + name: file.name,
  356 + size: file.size,
  357 + type: file.type,
  358 + status: status,
  359 + url: '',
  360 + fileId: ''
  361 + };
  362 +
  363 + if (type === 'before') {
  364 + this.beforeImages.push(fileItem);
  365 + } else {
  366 + this.afterImages.push(fileItem);
  367 + }
  368 + },
  369 +
  370 + // 更新文件状态
  371 + updateFileStatus(fileId, status, type, uploadData = null) {
  372 + const fileList = type === 'before' ? this.beforeImages : this.afterImages;
  373 + const fileIndex = fileList.findIndex(file => file.id === fileId);
  374 +
  375 + if (fileIndex !== -1) {
  376 + fileList[fileIndex].status = status;
  377 + if (uploadData) {
  378 + fileList[fileIndex].url = uploadData.url;
  379 + fileList[fileIndex].fileId = uploadData.name;
  380 + fileList[fileIndex].name = uploadData.name;
  381 + }
  382 + }
  383 + },
  384 +
  385 + // 删除文件
  386 + removeFile(type, index) {
  387 + if (type === 'before') {
  388 + this.beforeImages.splice(index, 1);
  389 + } else {
  390 + this.afterImages.splice(index, 1);
  391 + }
  392 + },
  393 +
  394 + // 表单提交
  395 + handleFormSubmit(event) {
  396 + event.preventDefault();
  397 + this.submitServiceLog();
  398 + },
  399 +
  400 + // 提交服务日志
  401 + async submitServiceLog() {
  402 + try {
  403 + // 验证表单
  404 + if (!this.formData.remark.trim()) {
  405 + uni.showToast({
  406 + title: '请输入反馈备注',
  407 + icon: 'none'
  408 + });
  409 + return;
  410 + }
  411 +
  412 + // 检查是否有图片/语音正在上传
  413 + const uploadingBefore = this.beforeImages.some(file => file.status === 'uploading');
  414 + const uploadingAfter = this.afterImages.some(file => file.status === 'uploading');
  415 + const uploadingVoice = this.voiceNote.uploading;
  416 +
  417 + if (uploadingBefore || uploadingAfter || uploadingVoice) {
  418 + uni.showToast({
  419 + title: '请等待图片/语音上传完成',
  420 + icon: 'none'
  421 + });
  422 + return;
  423 + }
  424 +
  425 + // 准备提交数据
  426 + const submitData = {
  427 + consumeId: this.consumeId,
  428 + beforeImage: JSON.stringify(this.beforeImages.filter(file => file.status === 'success').map(file => ({
  429 + name: file.name,
  430 + url: file.url,
  431 + fileId: file.fileId
  432 + }))),
  433 + afterImage: JSON.stringify(this.afterImages.filter(file => file.status === 'success').map(file => ({
  434 + name: file.name,
  435 + url: file.url,
  436 + fileId: file.fileId
  437 + }))),
  438 + remark: this.formData.remark,
  439 + kjbRemark: this.formData.kjbRemark,
  440 + // 语音备注(如果后端已支持会使用这些字段,未支持会忽略)
  441 + voiceUrl: this.voiceNote.url || '',
  442 + voiceDuration: this.voiceNote.duration || 0,
  443 + voiceFileId: this.voiceNote.fileId || ''
  444 + };
  445 +
  446 + this.isSubmitting = true;
  447 + uni.showLoading({
  448 + title: this.isEditMode ? '正在更新...' : '正在提交...'
  449 + });
  450 +
  451 + let result;
  452 + if (this.isEditMode && this.logId) {
  453 + // 编辑模式:调用更新接口
  454 + result = await lxApi.updateServiceLog({...submitData, id: this.logId});
  455 + } else {
  456 + // 新增模式:调用创建接口
  457 + result = await lxApi.createServiceLog(submitData);
  458 + }
  459 + uni.hideLoading();
  460 +
  461 + if (result.code === 200) {
  462 + uni.showToast({
  463 + title: this.isEditMode ? '服务日志更新成功!' : '服务日志提交成功!',
  464 + icon: 'success'
  465 + });
  466 +
  467 + // 返回上一页
  468 + setTimeout(() => {
  469 + uni.navigateBack({
  470 + delta: 1
  471 + });
  472 + }, 1500);
  473 + } else {
  474 + uni.showToast({
  475 + title: result.msg || (this.isEditMode ? '更新失败!' : '提交失败!'),
  476 + icon: 'none'
  477 + });
  478 + }
  479 + } catch (error) {
  480 + uni.hideLoading();
  481 + console.error('提交失败:', error);
  482 + uni.showToast({
  483 + title: '网络错误,请稍后重试',
  484 + icon: 'none'
  485 + });
  486 + } finally {
  487 + this.isSubmitting = false;
  488 + }
  489 + },
  490 +
  491 + // 初始化录音与播放
  492 + initRecorder() {
  493 + if (this.recorderInited) return
  494 + try {
  495 + // 录音管理器
  496 + this.recorderManager = uni.getRecorderManager()
  497 + this.recorderManager.onStart(() => {
  498 + this.voiceNote.status = 'recording'
  499 + })
  500 + this.recorderManager.onStop((res) => {
  501 + this.voiceNote.status = 'processing'
  502 + const tempFilePath = res.tempFilePath
  503 + const duration = res.duration || 0
  504 + if (!tempFilePath) {
  505 + this.voiceNote.status = 'idle'
  506 + uni.showToast({
  507 + title: '未获取到录音文件',
  508 + icon: 'none'
  509 + })
  510 + return
  511 + }
  512 + this.handleVoiceFile(tempFilePath, duration)
  513 + })
  514 + this.recorderManager.onError((err) => {
  515 + console.error('录音失败:', err)
  516 + this.voiceNote.status = 'idle'
  517 + uni.showToast({
  518 + title: '录音失败,请检查权限',
  519 + icon: 'none'
  520 + })
  521 + })
  522 +
  523 + // 播放器
  524 + this.audioContext = uni.createInnerAudioContext()
  525 + this.audioContext.onPlay(() => {
  526 + this.voiceNote.isPlaying = true
  527 + })
  528 + this.audioContext.onPause(() => {
  529 + this.voiceNote.isPlaying = false
  530 + })
  531 + this.audioContext.onStop(() => {
  532 + this.voiceNote.isPlaying = false
  533 + })
  534 + this.audioContext.onEnded(() => {
  535 + this.voiceNote.isPlaying = false
  536 + })
  537 +
  538 + this.recorderInited = true
  539 + } catch (e) {
  540 + console.error('初始化录音失败:', e)
  541 + }
  542 + },
  543 +
  544 + // 开始/停止录音
  545 + toggleRecord() {
  546 + if (!this.enableVoice) {
  547 + uni.showToast({
  548 + title: '当前环境暂不支持录音',
  549 + icon: 'none'
  550 + })
  551 + return
  552 + }
  553 + if (!this.recorderInited) {
  554 + this.initRecorder()
  555 + }
  556 + if (!this.recorderManager || typeof this.recorderManager.start !== 'function') {
  557 + uni.showToast({
  558 + title: '当前环境暂不支持录音',
  559 + icon: 'none'
  560 + })
  561 + return
  562 + }
  563 +
  564 + if (this.voiceNote.status === 'recording') {
  565 + // 停止录音
  566 + this.recorderManager.stop()
  567 + } else {
  568 + // 开始录音
  569 + this.voiceNote.status = 'recording'
  570 + this.voiceNote.duration = 0
  571 + this.voiceNote.uploading = false
  572 + try {
  573 + this.recorderManager.start({
  574 + duration: 600000, // 最长10分钟
  575 + format: 'mp3'
  576 + })
  577 + } catch (e) {
  578 + console.error('开始录音失败:', e)
  579 + this.voiceNote.status = 'idle'
  580 + uni.showToast({
  581 + title: '开始录音失败',
  582 + icon: 'none'
  583 + })
  584 + }
  585 + }
  586 + },
  587 +
  588 + // 处理录音文件
  589 + async handleVoiceFile(tempFilePath, duration) {
  590 + this.voiceNote.duration = Math.round(duration / 1000) // 毫秒转秒
  591 + this.voiceNote.uploading = true
  592 +
  593 + const file = {
  594 + path: tempFilePath,
  595 + tempFilePath,
  596 + size: 0,
  597 + name: `voice_${Date.now()}.mp3`,
  598 + type: 'audio/mp3'
  599 + }
  600 +
  601 + try {
  602 + const result = await lxApi.uploadFile(file)
  603 + if (result.code === 200 && result.data) {
  604 + this.voiceNote.url = result.data.url
  605 + this.voiceNote.fileId = result.data.name
  606 + this.voiceNote.status = 'recorded'
  607 + uni.showToast({
  608 + title: '语音上传成功',
  609 + icon: 'success'
  610 + })
  611 + } else {
  612 + throw new Error(result.msg || '上传失败')
  613 + }
  614 + } catch (error) {
  615 + console.error('语音上传失败:', error)
  616 + this.voiceNote.status = 'idle'
  617 + this.voiceNote.url = ''
  618 + this.voiceNote.fileId = ''
  619 + this.voiceNote.duration = 0
  620 + uni.showToast({
  621 + title: '语音上传失败',
  622 + icon: 'none'
  623 + })
  624 + } finally {
  625 + this.voiceNote.uploading = false
  626 + }
  627 + },
  628 +
  629 + // 播放/暂停语音
  630 + togglePlayVoice() {
  631 + if (!this.voiceNote.url) {
  632 + uni.showToast({
  633 + title: '暂无语音可播放',
  634 + icon: 'none'
  635 + })
  636 + return
  637 + }
  638 + if (!this.audioContext) {
  639 + this.initRecorder()
  640 + }
  641 + if (!this.audioContext) return
  642 +
  643 + if (this.voiceNote.isPlaying) {
  644 + this.audioContext.pause()
  645 + } else {
  646 + this.audioContext.stop()
  647 + const src = this.voiceNote.url.indexOf('http') === 0 ? this.voiceNote.url : (this.baseUrl + this.voiceNote.url)
  648 + this.audioContext.src = src
  649 + this.audioContext.play()
  650 + }
  651 + },
  652 +
  653 + // 删除语音
  654 + removeVoice() {
  655 + if (this.audioContext) {
  656 + this.audioContext.stop()
  657 + }
  658 + this.voiceNote = {
  659 + fileId: '',
  660 + url: '',
  661 + duration: 0,
  662 + status: 'idle',
  663 + uploading: false,
  664 + isPlaying: false
  665 + }
  666 + },
  667 +
  668 + // 语音时长显示
  669 + formatVoiceDuration(sec) {
  670 + const s = sec || 0
  671 + if (s < 60) {
  672 + return s + '秒'
  673 + }
  674 + const m = Math.floor(s / 60)
  675 + const left = s % 60
  676 + return m + '分' + (left ? left + '秒' : '')
  677 + }
  678 + }
  679 + }
  680 +</script>
  681 +
  682 +<style lang="scss" scoped>
  683 + .container {
  684 + // height: 100vh;
  685 + background: linear-gradient(135deg, #e8f5e9 0%, #b2dfdb 100%);
  686 + padding: 40rpx;
  687 + box-sizing: border-box;
  688 + }
  689 +
  690 + .member-card {
  691 + background: #fff;
  692 + border-radius: 32rpx;
  693 + box-shadow: 0 8rpx 32rpx rgba(76, 175, 80, 0.1);
  694 + padding: 40rpx;
  695 + margin-bottom: 40rpx;
  696 + }
  697 +
  698 + .member-info {
  699 + display: flex;
  700 + align-items: center;
  701 + }
  702 +
  703 + .member-avatar {
  704 + width: 80rpx;
  705 + height: 80rpx;
  706 + background: linear-gradient(135deg, #4caf50 0%, #66bb6a 100%);
  707 + border-radius: 50%;
  708 + display: flex;
  709 + align-items: center;
  710 + justify-content: center;
  711 + font-size: 40rpx;
  712 + margin-right: 24rpx;
  713 + }
  714 +
  715 + .member-details {
  716 + flex: 1;
  717 + }
  718 +
  719 + .member-name {
  720 + font-size: 32rpx;
  721 + font-weight: 600;
  722 + color: #2e7d32;
  723 + margin-bottom: 8rpx;
  724 + }
  725 +
  726 + .member-id {
  727 + font-size: 24rpx;
  728 + color: #6a9c6a;
  729 + }
  730 +
  731 + .form-card {
  732 + background: #fff;
  733 + border-radius: 32rpx;
  734 + box-shadow: 0 8rpx 32rpx rgba(76, 175, 80, 0.1);
  735 + overflow: hidden;
  736 + flex: 1;
  737 + }
  738 +
  739 + .form-content {
  740 + padding: 48rpx;
  741 + }
  742 +
  743 + .form-group {
  744 + margin-bottom: 40rpx;
  745 + }
  746 +
  747 + .form-group:last-child {
  748 + margin-bottom: 0;
  749 + }
  750 +
  751 + .form-label {
  752 + display: block;
  753 + margin-bottom: 16rpx;
  754 + font-weight: bold;
  755 + color: #388e3c;
  756 + letter-spacing: 2rpx;
  757 + font-size: 28rpx;
  758 + }
  759 +
  760 + .input-wrapper {
  761 + position: relative;
  762 + }
  763 +
  764 + .textarea-input {
  765 + background: #f9fff9;
  766 + border: 3rpx solid #c8e6c9;
  767 + border-radius: 20rpx;
  768 + padding: 24rpx;
  769 + font-size: 28rpx;
  770 + color: #2e7d32;
  771 + min-height: 200rpx;
  772 + box-sizing: border-box;
  773 + width: 100%;
  774 + }
  775 +
  776 + /* 文件上传样式 */
  777 + .file-upload-area {
  778 + border: 4rpx dashed #c8e6c9;
  779 + border-radius: 24rpx;
  780 + padding: 48rpx;
  781 + text-align: center;
  782 + background: linear-gradient(135deg, #f9fff9 0%, #e8f5e9 100%);
  783 + cursor: pointer;
  784 + min-height: 240rpx;
  785 + display: flex;
  786 + flex-direction: column;
  787 + justify-content: center;
  788 + position: relative;
  789 + overflow: hidden;
  790 + }
  791 +
  792 + .file-upload-text {
  793 + display: flex;
  794 + flex-direction: column;
  795 + align-items: center;
  796 + gap: 24rpx;
  797 + color: #6a9c6a;
  798 + font-size: 19rpx;
  799 + position: relative;
  800 + z-index: 1;
  801 + }
  802 +
  803 + .file-upload-icon {
  804 + font-size: 50rpx;
  805 + opacity: 0.8;
  806 + }
  807 +
  808 + .file-list {
  809 + margin-top: 32rpx;
  810 + text-align: left;
  811 + max-height: 400rpx;
  812 + overflow-y: auto;
  813 + }
  814 +
  815 + .file-item {
  816 + display: flex;
  817 + align-items: center;
  818 + justify-content: space-between;
  819 + padding: 20rpx 24rpx;
  820 + background: #fff;
  821 + border: 1rpx solid #e8f5e9;
  822 + border-radius: 12rpx;
  823 + margin-bottom: 12rpx;
  824 + font-size: 26rpx;
  825 + box-shadow: 0 2rpx 8rpx rgba(67, 233, 123, 0.08);
  826 + transition: all 0.3s ease;
  827 + }
  828 +
  829 + .file-info {
  830 + display: flex;
  831 + align-items: center;
  832 + gap: 20rpx;
  833 + overflow: hidden;
  834 + width: 40%;
  835 + }
  836 +
  837 + .file-icon {
  838 + font-size: 32rpx;
  839 + width: 56rpx;
  840 + height: 56rpx;
  841 + display: flex;
  842 + align-items: center;
  843 + justify-content: center;
  844 + background: linear-gradient(135deg, #f9fff9 0%, #e8f5e9 100%);
  845 + border-radius: 10rpx;
  846 + flex-shrink: 0;
  847 + border: 1rpx solid #c8e6c9;
  848 + }
  849 +
  850 + .file-name {
  851 + font-size: 28rpx;
  852 + color: #333;
  853 + font-weight: 500;
  854 + overflow: hidden;
  855 + text-overflow: ellipsis;
  856 + white-space: nowrap;
  857 + line-height: 1.4;
  858 + }
  859 +
  860 + .file-actions {
  861 + display: flex;
  862 + align-items: center;
  863 + gap: 12rpx;
  864 + flex-shrink: 0;
  865 + width: 55%;
  866 + }
  867 +
  868 + .file-status {
  869 + font-size: 22rpx;
  870 + padding: 6rpx 12rpx;
  871 + border-radius: 6rpx;
  872 + font-weight: 500;
  873 + white-space: nowrap;
  874 + }
  875 +
  876 + .file-status.success {
  877 + color: #2e7d32;
  878 + background: #e8f5e9;
  879 + border: 1rpx solid #c8e6c9;
  880 + }
  881 +
  882 + .file-status.error {
  883 + color: #c62828;
  884 + background: #ffebee;
  885 + border: 1rpx solid #ffcdd2;
  886 + }
  887 +
  888 + .file-status.uploading {
  889 + color: #ef6c00;
  890 + background: #fff3e0;
  891 + border: 1rpx solid #ffe0b2;
  892 + }
  893 +
  894 + .btn-error {
  895 + background: #f44336;
  896 + color: #fff;
  897 + border: none;
  898 + border-radius: 6rpx;
  899 + font-size: 20rpx;
  900 + }
  901 +
  902 + .btn-group {
  903 + display: flex;
  904 + gap: 24rpx;
  905 + margin-top: 48rpx;
  906 + }
  907 +
  908 + .btn {
  909 + flex: 1;
  910 + padding: 10rpx 40rpx;
  911 + border: none;
  912 + border-radius: 20rpx;
  913 + font-size: 28rpx;
  914 + font-weight: bold;
  915 + cursor: pointer;
  916 + transition: all 0.2s ease;
  917 + letter-spacing: 2rpx;
  918 + }
  919 +
  920 + .btn-primary {
  921 + background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
  922 + color: #fff;
  923 + box-shadow: 0 4rpx 16rpx rgba(67, 233, 123, 0.3);
  924 + }
  925 +
  926 + .btn-primary:hover {
  927 + box-shadow: 0 8rpx 32rpx rgba(67, 233, 123, 0.4);
  928 + transform: translateY(-2rpx);
  929 + }
  930 +
  931 + /* 语音备注样式 */
  932 + .voice-card {
  933 + background: radial-gradient(circle at 0 0, rgba(129, 199, 132, 0.12), transparent 60%),
  934 + radial-gradient(circle at 100% 100%, rgba(129, 212, 250, 0.16), transparent 55%),
  935 + #f9fff9;
  936 + border-radius: 24rpx;
  937 + border: 2rpx solid #c8e6c9;
  938 + padding: 24rpx 28rpx;
  939 + display: flex;
  940 + flex-direction: column;
  941 + gap: 24rpx;
  942 + position: relative;
  943 + overflow: hidden;
  944 + }
  945 +
  946 + .voice-card::before {
  947 + content: '';
  948 + position: absolute;
  949 + left: -40%;
  950 + top: 0;
  951 + width: 80%;
  952 + height: 100%;
  953 + background: linear-gradient(120deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, .65), rgba(255, 255, 255, 0));
  954 + transform: skewX(-20deg);
  955 + animation: voiceShine 4s infinite;
  956 + pointer-events: none;
  957 + }
  958 +
  959 + .voice-card--disabled {
  960 + opacity: 0.8;
  961 + }
  962 +
  963 + .voice-main {
  964 + display: flex;
  965 + align-items: center;
  966 + gap: 24rpx;
  967 + }
  968 +
  969 + .voice-record-btn {
  970 + width: 96rpx;
  971 + height: 96rpx;
  972 + border-radius: 50%;
  973 + background: radial-gradient(circle at 30% 30%, #ffffff 0%, #81c784 60%, #4caf50 100%);
  974 + display: flex;
  975 + align-items: center;
  976 + justify-content: center;
  977 + box-shadow: 0 8rpx 20rpx rgba(76, 175, 80, 0.4);
  978 + transition: all 0.2s ease;
  979 + position: relative;
  980 + }
  981 +
  982 + .voice-record-btn::after {
  983 + content: '';
  984 + position: absolute;
  985 + inset: 0;
  986 + border-radius: inherit;
  987 + border: 2rpx solid rgba(255, 255, 255, 0.7);
  988 + opacity: 0;
  989 + }
  990 +
  991 + /* 默认轻微呼吸动画 */
  992 + .voice-record-btn {
  993 + animation: voiceBreath 2.4s ease-in-out infinite;
  994 + }
  995 +
  996 + .voice-record-btn.recording {
  997 + background: radial-gradient(circle at 30% 30%, #ffffff 0%, #ff8a80 60%, #e53935 100%);
  998 + box-shadow: 0 10rpx 26rpx rgba(229, 57, 53, 0.5);
  999 + transform: scale(1.05);
  1000 + animation: voicePulse 1.2s ease-out infinite;
  1001 + }
  1002 +
  1003 + .voice-record-btn.recording::after {
  1004 + animation: voiceHalo 1.2s ease-out infinite;
  1005 + }
  1006 +
  1007 + .voice-record-icon {
  1008 + font-size: 44rpx;
  1009 + }
  1010 +
  1011 + .voice-info {
  1012 + display: flex;
  1013 + flex-direction: column;
  1014 + gap: 8rpx;
  1015 + }
  1016 +
  1017 + .voice-status-text {
  1018 + font-size: 26rpx;
  1019 + color: #388e3c;
  1020 + font-weight: 500;
  1021 + transition: color 0.2s ease;
  1022 + }
  1023 +
  1024 + .voice-record-btn.recording + .voice-info .voice-status-text {
  1025 + color: #e53935;
  1026 + }
  1027 +
  1028 + .voice-duration {
  1029 + font-size: 22rpx;
  1030 + color: #6a9c6a;
  1031 + }
  1032 +
  1033 + .voice-actions {
  1034 + margin-top: 8rpx;
  1035 + display: flex;
  1036 + align-items: center;
  1037 + gap: 20rpx;
  1038 + }
  1039 +
  1040 + .voice-wave {
  1041 + display: flex;
  1042 + align-items: flex-end;
  1043 + gap: 4rpx;
  1044 + height: 32rpx;
  1045 + }
  1046 +
  1047 + .wave-bar {
  1048 + width: 4rpx;
  1049 + height: 8rpx;
  1050 + border-radius: 999rpx;
  1051 + background: #a5d6a7;
  1052 + transition: height 0.2s ease;
  1053 + }
  1054 +
  1055 + .voice-wave.playing .wave-bar:nth-child(1) {
  1056 + animation: voiceWave 0.6s infinite ease-in-out;
  1057 + }
  1058 +
  1059 + .voice-wave.playing .wave-bar:nth-child(2) {
  1060 + animation: voiceWave 0.6s 0.1s infinite ease-in-out;
  1061 + }
  1062 +
  1063 + .voice-wave.playing .wave-bar:nth-child(3) {
  1064 + animation: voiceWave 0.6s 0.2s infinite ease-in-out;
  1065 + }
  1066 +
  1067 + .voice-wave.playing .wave-bar:nth-child(4) {
  1068 + animation: voiceWave 0.6s 0.3s infinite ease-in-out;
  1069 + }
  1070 +
  1071 + .voice-wave.playing .wave-bar:nth-child(5) {
  1072 + animation: voiceWave 0.6s 0.4s infinite ease-in-out;
  1073 + }
  1074 +
  1075 + @keyframes voiceWave {
  1076 + 0%, 100% {
  1077 + height: 8rpx;
  1078 + background: #a5d6a7;
  1079 + }
  1080 + 50% {
  1081 + height: 28rpx;
  1082 + background: #43a047;
  1083 + }
  1084 + }
  1085 +
  1086 + @keyframes voiceBreath {
  1087 + 0%, 100% {
  1088 + transform: scale(1);
  1089 + box-shadow: 0 8rpx 20rpx rgba(76, 175, 80, 0.4);
  1090 + }
  1091 + 50% {
  1092 + transform: scale(1.03);
  1093 + box-shadow: 0 10rpx 26rpx rgba(76, 175, 80, 0.55);
  1094 + }
  1095 + }
  1096 +
  1097 + @keyframes voicePulse {
  1098 + 0% {
  1099 + transform: scale(1.02);
  1100 + box-shadow: 0 10rpx 26rpx rgba(229, 57, 53, 0.55);
  1101 + }
  1102 + 50% {
  1103 + transform: scale(1.12);
  1104 + box-shadow: 0 16rpx 40rpx rgba(229, 57, 53, 0.6);
  1105 + }
  1106 + 100% {
  1107 + transform: scale(1.02);
  1108 + box-shadow: 0 10rpx 26rpx rgba(229, 57, 53, 0.55);
  1109 + }
  1110 + }
  1111 +
  1112 + @keyframes voiceHalo {
  1113 + 0% {
  1114 + transform: scale(1);
  1115 + opacity: 0.7;
  1116 + }
  1117 + 100% {
  1118 + transform: scale(1.4);
  1119 + opacity: 0;
  1120 + }
  1121 + }
  1122 +
  1123 + @keyframes voiceShine {
  1124 + 0% {
  1125 + transform: translateX(-120%) skewX(-20deg);
  1126 + opacity: 0;
  1127 + }
  1128 + 20% {
  1129 + opacity: 1;
  1130 + }
  1131 + 60% {
  1132 + transform: translateX(140%) skewX(-20deg);
  1133 + opacity: 0;
  1134 + }
  1135 + 100% {
  1136 + transform: translateX(140%) skewX(-20deg);
  1137 + opacity: 0;
  1138 + }
  1139 + }
  1140 +
  1141 + .btn-plain {
  1142 + background: #ffffff;
  1143 + color: #388e3c;
  1144 + border: 2rpx solid #c8e6c9;
  1145 + border-radius: 12rpx;
  1146 + padding: 8rpx 20rpx;
  1147 + font-size: 24rpx;
  1148 + }
  1149 +</style>
... ...
绿纤uni-app/pages/expansion/expansion.vue
... ... @@ -54,8 +54,8 @@
54 54 </u-form-item>
55 55  
56 56 <!-- 提交按钮 -->
57   - <u-button type="primary" :loading="loading" :disabled="loading" @click="loading?null:handleFormSubmit()"
58   - :customStyle="buttonStyle">
  57 + <u-button type="primary" :loading="loading" :disabled="loading"
  58 + @click="loading?null:handleFormSubmit()" :customStyle="buttonStyle">
59 59 {{ loading ? '提交中...' : '保存' }}
60 60 </u-button>
61 61 </u-form>
... ... @@ -129,15 +129,24 @@
129 129 message: '请输入顾客姓名',
130 130 trigger: 'blur'
131 131 }],
132   - customerPhone: [{
  132 + customerPhone: [
  133 + {
133 134 required: true,
134 135 message: '请输入电话号码',
135   - trigger: 'blur'
  136 + trigger: ['blur', 'change']
136 137 },
137 138 {
138   - pattern: /^1[3-9]\d{9}$/,
139   - message: '请输入正确的手机号码',
140   - trigger: 'blur'
  139 + validator: (rule, value, callback) => {
  140 + const phone = String(value || '').trim()
  141 + if (!phone) {
  142 + callback(new Error('请输入电话号码'))
  143 + } else if (!/^1[3-9]\d{9}$/.test(phone)) {
  144 + callback(new Error('请输入正确的手机号码'))
  145 + } else {
  146 + callback()
  147 + }
  148 + },
  149 + trigger: ['blur', 'change']
141 150 }
142 151 ],
143 152 paymentMethod: [{
... ... @@ -327,16 +336,25 @@
327 336  
328 337 // 处理表单提交
329 338 async handleFormSubmit() {
330   - console.error('【【【【【【')
331 339 try {
332   - // 使用 uview 表单验证
  340 + // 先走 uview 自带必填等校验
333 341 await this.$refs.form.validate()
334 342  
  343 + // 再做一层自定义业务校验(尤其是手机号)
  344 + const errors = await this.validateForm()
  345 + if (errors && errors.length > 0) {
  346 + uni.showToast({
  347 + title: errors[0],
  348 + icon: 'none',
  349 + duration: 2000
  350 + })
  351 + return
  352 + }
  353 +
335 354 // 收集表单数据
336 355 const formData = this.collectFormData()
337 356 console.log('表单数据:', formData)
338 357  
339   - // 提交数据
340 358 await this.submitExpansion(formData)
341 359 } catch (error) {
342 360 console.log('表单验证失败:', error)
... ... @@ -355,7 +373,8 @@
355 373 // console.error(pushUrl)
356 374 // return
357 375 const res = await this.API.submitExpansion(data)
358   -
  376 + // console.error(res)
  377 + // return
359 378 if (res.code === 200 && res.data && res.data.entity) {
360 379 let tkinfo = res.data
361 380  
... ... @@ -390,7 +409,7 @@
390 409 // 格式化时间
391 410 const formatTime = (timestamp) => {
392 411 const date = new Date(timestamp)
393   - return uni.$u.timeFormat(date,'yyyy/mm/dd hh:MM')
  412 + return uni.$u.timeFormat(date, 'yyyy/mm/dd hh:MM')
394 413 }
395 414  
396 415 const datanew = {
... ...
绿纤uni-app/unpackage/dist/build/mp-weixin/pages/expansion/expansion.js
1   -(global["webpackJsonp"]=global["webpackJsonp"]||[]).push([["pages/expansion/expansion"],{"0ac1":function(e,t,n){"use strict";n.r(t);var r=n("20b8"),a=n("2242");for(var o in a)["default"].indexOf(o)<0&&function(e){n.d(t,e,(function(){return a[e]}))}(o);n("9854");var i=n("828b"),u=Object(i["a"])(a["default"],r["b"],r["c"],!1,null,"6122c20a",null,!1,r["a"],void 0);t["default"]=u.exports},"20b8":function(e,t,n){"use strict";n.d(t,"b",(function(){return a})),n.d(t,"c",(function(){return o})),n.d(t,"a",(function(){return r}));var r={uForm:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-form/u-form")]).then(n.bind(null,"63f8"))},uFormItem:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-form-item/u-form-item")]).then(n.bind(null,"eabc"))},uPicker:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-picker/u-picker")]).then(n.bind(null,"0e74"))},uIcon:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-icon/u-icon")]).then(n.bind(null,"3f69"))},uInput:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-input/u-input")]).then(n.bind(null,"5f80"))},uTextarea:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-textarea/u-textarea")]).then(n.bind(null,"269f"))},uButton:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-button/u-button")]).then(n.bind(null,"11af"))}},a=function(){var e=this,t=e.$createElement;e._self._c;e._isMounted||(e.e0=function(t){e.showEventPicker=!1},e.e1=function(t){e.showEventPicker=!1},e.e2=function(t){e.showEventPicker=!0},e.e3=function(t){e.showPaymentPicker=!1},e.e4=function(t){e.showPaymentPicker=!1},e.e5=function(t){e.showPaymentPicker=!0},e.e6=function(t){e.showWeChatPicker=!1},e.e7=function(t){e.showWeChatPicker=!1},e.e8=function(t){e.showWeChatPicker=!0},e.e9=function(t){!e.loading&&e.handleFormSubmit()})},o=[]},2242:function(e,t,n){"use strict";n.r(t);var r=n("2567"),a=n.n(r);for(var o in r)["default"].indexOf(o)<0&&function(e){n.d(t,e,(function(){return r[e]}))}(o);t["default"]=a.a},2567:function(e,t,n){"use strict";(function(e){var r=n("47a9");Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=r(n("7eb4")),o=r(n("ee10")),i=(n("8f59"),{data:function(){return{loading:!1,userInfo:null,currentEventId:null,eventList:[],formData:{eventId:"",customerName:"",customerPhone:"",paymentMethod:"微信",isAddWeChat:"否",remarks:""},showEventPicker:!1,showPaymentPicker:!1,showWeChatPicker:!1,selectedEventName:"",paymentOptions:[{text:"微信",value:"微信"},{text:"支付宝",value:"支付宝"},{text:"现金",value:"现金"},{text:"银行转账",value:"银行转账"}],weChatOptions:[{text:"是",value:"是"},{text:"否",value:"否"}],rules:{eventId:[{required:!0,message:"请选择拓客活动",trigger:"change"}],customerName:[{required:!0,message:"请输入顾客姓名",trigger:"blur"}],customerPhone:[{required:!0,message:"请输入电话号码",trigger:"blur"},{pattern:/^1[3-9]\d{9}$/,message:"请输入正确的手机号码",trigger:"blur"}],paymentMethod:[{required:!0,message:"请选择支付方式",trigger:"change"}],isAddWeChat:[{required:!0,message:"请选择是否加微信",trigger:"change"}]},inputStyle:{backgroundColor:"#f9fff9",border:"3rpx solid #c8e6c9",borderRadius:"20rpx",padding:"24rpx",fontSize:"28rpx",color:"#2e7d32"},textareaStyle:{backgroundColor:"#f9fff9",border:"3rpx solid #c8e6c9",borderRadius:"20rpx",padding:"32rpx",fontSize:"28rpx",color:"#2e7d32"},buttonStyle:{width:"100%",height:"80rpx",borderRadius:"24rpx",fontSize:"32rpx",fontWeight:"600",letterSpacing:"2rpx",background:"linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)",boxShadow:"0 8rpx 32rpx rgba(67, 233, 123, 0.3)",marginTop:"64rpx",borderColor:"rgba(67, 233, 123, 0.3)"}}},onLoad:function(){this.initializePage()},methods:{initializePage:function(){var t=this;return(0,o.default)(a.default.mark((function n(){return a.default.wrap((function(n){while(1)switch(n.prev=n.next){case 0:if(n.prev=0,t.userInfo=e.getStorageSync("userInfo"),t.userInfo&&0!==Object.keys(t.userInfo).length){n.next=5;break}return e.reLaunch({url:"/pages/login/login"}),n.abrupt("return");case 5:return console.log("用户信息:",t.userInfo),t.setDefaultDateTime(),n.next=9,t.getCurrentEvent();case 9:n.next=15;break;case 11:n.prev=11,n.t0=n["catch"](0),console.error("页面初始化失败:",n.t0),e.showToast({title:"页面初始化失败,请刷新重试",icon:"none",duration:3e3});case 15:case"end":return n.stop()}}),n,null,[[0,11]])})))()},setDefaultDateTime:function(){this.formData.paymentMethod="微信",this.formData.isAddWeChat="否"},getCurrentEvent:function(){var e=this;return(0,o.default)(a.default.mark((function t(){var n;return a.default.wrap((function(t){while(1)switch(t.prev=t.next){case 0:return t.prev=0,t.next=3,e.API.getCurrentEvent(e.userInfo.userId);case 3:n=t.sent,200===n.code&&n.data&&n.data.length>0?(e.eventList=n.data.map((function(e){return{text:e.EventName,value:e.EventId}})),n.data.length>0&&(e.currentEventId=n.data[0].EventId,e.formData.eventId=e.currentEventId,e.selectedEventName=n.data[0].EventName,console.log("当前活动ID:",e.currentEventId))):console.warn("未找到当前活动"),t.next=10;break;case 7:t.prev=7,t.t0=t["catch"](0),console.error("获取当前活动失败:",t.t0);case 10:case"end":return t.stop()}}),t,null,[[0,7]])})))()},validateForm:function(){var e=this;return(0,o.default)(a.default.mark((function t(){var n,r,o;return a.default.wrap((function(t){while(1)switch(t.prev=t.next){case 0:return n=[],e.formData.eventId||n.push("请选择拓客活动"),e.formData.customerName.trim()||n.push("请输入顾客姓名"),r=e.formData.customerPhone.trim(),r?(o=/^1[3-9]\d{9}$/,o.test(r)||n.push("请输入正确的手机号码")):n.push("请输入电话号码"),e.formData.paymentMethod||n.push("请选择支付方式"),e.formData.isAddWeChat||n.push("请选择是否加微信"),t.abrupt("return",n);case 8:case"end":return t.stop()}}),t)})))()},collectFormData:function(){return{expansionTime:null,customerName:this.formData.customerName.trim(),customerPhone:this.formData.customerPhone.trim(),buyNumber:1,paymentMethod:this.formData.paymentMethod,isAddWeChat:this.formData.isAddWeChat,remarks:this.formData.remarks.trim(),expansionUserId:this.userInfo.userId,eventId:this.formData.eventId}},onEventConfirm:function(e){var t=e.value;this.formData.eventId=t[0].value,this.selectedEventName=t[0].text,this.showEventPicker=!1},onPaymentConfirm:function(e){var t=e.value;this.formData.paymentMethod=t[0].value,this.showPaymentPicker=!1},onWeChatConfirm:function(e){var t=e.value;this.formData.isAddWeChat=t[0].value,this.showWeChatPicker=!1},handleFormSubmit:function(){var e=this;return(0,o.default)(a.default.mark((function t(){var n;return a.default.wrap((function(t){while(1)switch(t.prev=t.next){case 0:return console.error("【【【【【【"),t.prev=1,t.next=4,e.$refs.form.validate();case 4:return n=e.collectFormData(),console.log("表单数据:",n),t.next=8,e.submitExpansion(n);case 8:t.next=13;break;case 10:t.prev=10,t.t0=t["catch"](1),console.log("表单验证失败:",t.t0);case 13:case"end":return t.stop()}}),t,null,[[1,10]])})))()},submitExpansion:function(t){var n=this;return(0,o.default)(a.default.mark((function r(){var o,i,u,c,s;return a.default.wrap((function(r){while(1)switch(r.prev=r.next){case 0:return r.prev=0,e.showLoading({title:"正在提交..."}),n.loading=!0,r.next=5,n.API.getEvent(t.eventId);case 5:return o=r.sent,i=o.data&&o.data.pushUrl?o.data.pushUrl:"",r.next=9,n.API.submitExpansion(t);case 9:if(u=r.sent,200!==u.code||!u.data||!u.data.entity){r.next=16;break}return c=u.data,r.next=14,n.sendWeChatNotification(c,t,i);case 14:r.next=19;break;case 16:e.hideLoading(),e.showToast({title:"添加失败:".concat((null===(s=u.data)||void 0===s?void 0:s.Message)||"未知错误"),icon:"none",duration:3e3}),n.loading=!1;case 19:r.next=27;break;case 21:r.prev=21,r.t0=r["catch"](0),console.error("提交失败:",r.t0),e.hideLoading(),e.showToast({title:"网络错误,请稍后重试",icon:"none",duration:3e3}),n.loading=!1;case 27:case"end":return r.stop()}}),r,null,[[0,21]])})))()},sendWeChatNotification:function(t,n,r){var i=this;return(0,o.default)(a.default.mark((function o(){var u,c,s,d,l;return a.default.wrap((function(a){while(1)switch(a.prev=a.next){case 0:return a.prev=0,d=function(t){var n=new Date(t);return e.$u.timeFormat(n,"yyyy/mm/dd hh:MM")},l={webhookUrl:r||"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=581c22a6-cb67-42e5-8c76-b8e90052e188",content:"🎉 拓客记录\n⏩门店:".concat((null===(u=t.storeinfo)||void 0===u?void 0:u.Dm)||"未知","\n⏩拓客人员:").concat(i.userInfo.userName||"未知","\n⏩战队:").concat((null===(c=t.entity)||void 0===c?void 0:c.TeamName)||"未知","\n⏩顾客姓名:").concat(n.customerName,"\n⏩电话号码:").concat(n.customerPhone,"\n⏩购买张数:").concat(n.buyNumber,"\n⏩拓客时间:").concat(d(null===(s=t.entity)||void 0===s?void 0:s.ExpansionTime),"\n⏩支付方式:").concat(n.paymentMethod,"\n⏩是否加微信:").concat(n.isAddWeChat,"\n⏩备注:").concat(n.remarks||"")},a.next=5,i.API.sendWeChatNotification(l);case 5:e.hideLoading(),e.showToast({title:"拓客数据提交成功!",icon:"success",duration:2e3}),i.loading=!1,i.clearForm(),a.next=14;break;case 11:a.prev=11,a.t0=a["catch"](0),console.error("发送微信通知失败:",a.t0);case 14:case"end":return a.stop()}}),o,null,[[0,11]])})))()},clearForm:function(){var e=this;try{if(this.formData={eventId:this.currentEventId||"",customerName:"",customerPhone:"",paymentMethod:"微信",isAddWeChat:"否",remarks:""},this.currentEventId&&this.eventList.length>0){var t=this.eventList.find((function(t){return t.value===e.currentEventId}));this.selectedEventName=t?t.text:""}else this.selectedEventName="";this.$refs.form&&this.$refs.form.clearValidate(),console.log("表单已清空")}catch(n){console.error("清空表单失败:",n)}}}});t.default=i}).call(this,n("df3c")["default"])},"34c6":function(e,t,n){},9854:function(e,t,n){"use strict";var r=n("34c6"),a=n.n(r);a.a},e61b:function(e,t,n){"use strict";(function(e,t){var r=n("47a9");n("2fec");r(n("3240"));var a=r(n("0ac1"));e.__webpack_require_UNI_MP_PLUGIN__=n,t(a.default)}).call(this,n("3223")["default"],n("df3c")["createPage"])}},[["e61b","common/runtime","common/vendor"]]]);
2 1 \ No newline at end of file
  2 +(global["webpackJsonp"]=global["webpackJsonp"]||[]).push([["pages/expansion/expansion"],{"0ac1":function(e,t,n){"use strict";n.r(t);var r=n("79c0"),a=n("2242");for(var o in a)["default"].indexOf(o)<0&&function(e){n.d(t,e,(function(){return a[e]}))}(o);n("a2de");var i=n("828b"),u=Object(i["a"])(a["default"],r["b"],r["c"],!1,null,"05d8df92",null,!1,r["a"],void 0);t["default"]=u.exports},2242:function(e,t,n){"use strict";n.r(t);var r=n("2567"),a=n.n(r);for(var o in r)["default"].indexOf(o)<0&&function(e){n.d(t,e,(function(){return r[e]}))}(o);t["default"]=a.a},2567:function(e,t,n){"use strict";(function(e){var r=n("47a9");Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=r(n("7eb4")),o=r(n("ee10")),i=(n("8f59"),{data:function(){return{loading:!1,userInfo:null,currentEventId:null,eventList:[],formData:{eventId:"",customerName:"",customerPhone:"",paymentMethod:"微信",isAddWeChat:"否",remarks:""},showEventPicker:!1,showPaymentPicker:!1,showWeChatPicker:!1,selectedEventName:"",paymentOptions:[{text:"微信",value:"微信"},{text:"支付宝",value:"支付宝"},{text:"现金",value:"现金"},{text:"银行转账",value:"银行转账"}],weChatOptions:[{text:"是",value:"是"},{text:"否",value:"否"}],rules:{eventId:[{required:!0,message:"请选择拓客活动",trigger:"change"}],customerName:[{required:!0,message:"请输入顾客姓名",trigger:"blur"}],customerPhone:[{required:!0,message:"请输入电话号码",trigger:["blur","change"]},{validator:function(e,t,n){var r=String(t||"").trim();r?/^1[3-9]\d{9}$/.test(r)?n():n(new Error("请输入正确的手机号码")):n(new Error("请输入电话号码"))},trigger:["blur","change"]}],paymentMethod:[{required:!0,message:"请选择支付方式",trigger:"change"}],isAddWeChat:[{required:!0,message:"请选择是否加微信",trigger:"change"}]},inputStyle:{backgroundColor:"#f9fff9",border:"3rpx solid #c8e6c9",borderRadius:"20rpx",padding:"24rpx",fontSize:"28rpx",color:"#2e7d32"},textareaStyle:{backgroundColor:"#f9fff9",border:"3rpx solid #c8e6c9",borderRadius:"20rpx",padding:"32rpx",fontSize:"28rpx",color:"#2e7d32"},buttonStyle:{width:"100%",height:"80rpx",borderRadius:"24rpx",fontSize:"32rpx",fontWeight:"600",letterSpacing:"2rpx",background:"linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)",boxShadow:"0 8rpx 32rpx rgba(67, 233, 123, 0.3)",marginTop:"64rpx",borderColor:"rgba(67, 233, 123, 0.3)"}}},onLoad:function(){this.initializePage()},methods:{initializePage:function(){var t=this;return(0,o.default)(a.default.mark((function n(){return a.default.wrap((function(n){while(1)switch(n.prev=n.next){case 0:if(n.prev=0,t.userInfo=e.getStorageSync("userInfo"),t.userInfo&&0!==Object.keys(t.userInfo).length){n.next=5;break}return e.reLaunch({url:"/pages/login/login"}),n.abrupt("return");case 5:return console.log("用户信息:",t.userInfo),t.setDefaultDateTime(),n.next=9,t.getCurrentEvent();case 9:n.next=15;break;case 11:n.prev=11,n.t0=n["catch"](0),console.error("页面初始化失败:",n.t0),e.showToast({title:"页面初始化失败,请刷新重试",icon:"none",duration:3e3});case 15:case"end":return n.stop()}}),n,null,[[0,11]])})))()},setDefaultDateTime:function(){this.formData.paymentMethod="微信",this.formData.isAddWeChat="否"},getCurrentEvent:function(){var e=this;return(0,o.default)(a.default.mark((function t(){var n;return a.default.wrap((function(t){while(1)switch(t.prev=t.next){case 0:return t.prev=0,t.next=3,e.API.getCurrentEvent(e.userInfo.userId);case 3:n=t.sent,200===n.code&&n.data&&n.data.length>0?(e.eventList=n.data.map((function(e){return{text:e.EventName,value:e.EventId}})),n.data.length>0&&(e.currentEventId=n.data[0].EventId,e.formData.eventId=e.currentEventId,e.selectedEventName=n.data[0].EventName,console.log("当前活动ID:",e.currentEventId))):console.warn("未找到当前活动"),t.next=10;break;case 7:t.prev=7,t.t0=t["catch"](0),console.error("获取当前活动失败:",t.t0);case 10:case"end":return t.stop()}}),t,null,[[0,7]])})))()},validateForm:function(){var e=this;return(0,o.default)(a.default.mark((function t(){var n,r,o;return a.default.wrap((function(t){while(1)switch(t.prev=t.next){case 0:return n=[],e.formData.eventId||n.push("请选择拓客活动"),e.formData.customerName.trim()||n.push("请输入顾客姓名"),r=e.formData.customerPhone.trim(),r?(o=/^1[3-9]\d{9}$/,o.test(r)||n.push("请输入正确的手机号码")):n.push("请输入电话号码"),e.formData.paymentMethod||n.push("请选择支付方式"),e.formData.isAddWeChat||n.push("请选择是否加微信"),t.abrupt("return",n);case 8:case"end":return t.stop()}}),t)})))()},collectFormData:function(){return{expansionTime:null,customerName:this.formData.customerName.trim(),customerPhone:this.formData.customerPhone.trim(),buyNumber:1,paymentMethod:this.formData.paymentMethod,isAddWeChat:this.formData.isAddWeChat,remarks:this.formData.remarks.trim(),expansionUserId:this.userInfo.userId,eventId:this.formData.eventId}},onEventConfirm:function(e){var t=e.value;this.formData.eventId=t[0].value,this.selectedEventName=t[0].text,this.showEventPicker=!1},onPaymentConfirm:function(e){var t=e.value;this.formData.paymentMethod=t[0].value,this.showPaymentPicker=!1},onWeChatConfirm:function(e){var t=e.value;this.formData.isAddWeChat=t[0].value,this.showWeChatPicker=!1},handleFormSubmit:function(){var t=this;return(0,o.default)(a.default.mark((function n(){var r,o;return a.default.wrap((function(n){while(1)switch(n.prev=n.next){case 0:return n.prev=0,n.next=3,t.$refs.form.validate();case 3:return n.next=5,t.validateForm();case 5:if(r=n.sent,!(r&&r.length>0)){n.next=9;break}return e.showToast({title:r[0],icon:"none",duration:2e3}),n.abrupt("return");case 9:return o=t.collectFormData(),console.log("表单数据:",o),n.next=13,t.submitExpansion(o);case 13:n.next=18;break;case 15:n.prev=15,n.t0=n["catch"](0),console.log("表单验证失败:",n.t0);case 18:case"end":return n.stop()}}),n,null,[[0,15]])})))()},submitExpansion:function(t){var n=this;return(0,o.default)(a.default.mark((function r(){var o,i,u,c,s;return a.default.wrap((function(r){while(1)switch(r.prev=r.next){case 0:return r.prev=0,e.showLoading({title:"正在提交..."}),n.loading=!0,r.next=5,n.API.getEvent(t.eventId);case 5:return o=r.sent,i=o.data&&o.data.pushUrl?o.data.pushUrl:"",r.next=9,n.API.submitExpansion(t);case 9:if(u=r.sent,200!==u.code||!u.data||!u.data.entity){r.next=16;break}return c=u.data,r.next=14,n.sendWeChatNotification(c,t,i);case 14:r.next=19;break;case 16:e.hideLoading(),e.showToast({title:"添加失败:".concat((null===(s=u.data)||void 0===s?void 0:s.Message)||"未知错误"),icon:"none",duration:3e3}),n.loading=!1;case 19:r.next=27;break;case 21:r.prev=21,r.t0=r["catch"](0),console.error("提交失败:",r.t0),e.hideLoading(),e.showToast({title:"网络错误,请稍后重试",icon:"none",duration:3e3}),n.loading=!1;case 27:case"end":return r.stop()}}),r,null,[[0,21]])})))()},sendWeChatNotification:function(t,n,r){var i=this;return(0,o.default)(a.default.mark((function o(){var u,c,s,d,l;return a.default.wrap((function(a){while(1)switch(a.prev=a.next){case 0:return a.prev=0,d=function(t){var n=new Date(t);return e.$u.timeFormat(n,"yyyy/mm/dd hh:MM")},l={webhookUrl:r||"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=581c22a6-cb67-42e5-8c76-b8e90052e188",content:"🎉 拓客记录\n⏩门店:".concat((null===(u=t.storeinfo)||void 0===u?void 0:u.Dm)||"未知","\n⏩拓客人员:").concat(i.userInfo.userName||"未知","\n⏩战队:").concat((null===(c=t.entity)||void 0===c?void 0:c.TeamName)||"未知","\n⏩顾客姓名:").concat(n.customerName,"\n⏩电话号码:").concat(n.customerPhone,"\n⏩购买张数:").concat(n.buyNumber,"\n⏩拓客时间:").concat(d(null===(s=t.entity)||void 0===s?void 0:s.ExpansionTime),"\n⏩支付方式:").concat(n.paymentMethod,"\n⏩是否加微信:").concat(n.isAddWeChat,"\n⏩备注:").concat(n.remarks||"")},a.next=5,i.API.sendWeChatNotification(l);case 5:e.hideLoading(),e.showToast({title:"拓客数据提交成功!",icon:"success",duration:2e3}),i.loading=!1,i.clearForm(),a.next=14;break;case 11:a.prev=11,a.t0=a["catch"](0),console.error("发送微信通知失败:",a.t0);case 14:case"end":return a.stop()}}),o,null,[[0,11]])})))()},clearForm:function(){var e=this;try{if(this.formData={eventId:this.currentEventId||"",customerName:"",customerPhone:"",paymentMethod:"微信",isAddWeChat:"否",remarks:""},this.currentEventId&&this.eventList.length>0){var t=this.eventList.find((function(t){return t.value===e.currentEventId}));this.selectedEventName=t?t.text:""}else this.selectedEventName="";this.$refs.form&&this.$refs.form.clearValidate(),console.log("表单已清空")}catch(n){console.error("清空表单失败:",n)}}}});t.default=i}).call(this,n("df3c")["default"])},7445:function(e,t,n){},"79c0":function(e,t,n){"use strict";n.d(t,"b",(function(){return a})),n.d(t,"c",(function(){return o})),n.d(t,"a",(function(){return r}));var r={uForm:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-form/u-form")]).then(n.bind(null,"63f8"))},uFormItem:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-form-item/u-form-item")]).then(n.bind(null,"eabc"))},uPicker:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-picker/u-picker")]).then(n.bind(null,"0e74"))},uIcon:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-icon/u-icon")]).then(n.bind(null,"3f69"))},uInput:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-input/u-input")]).then(n.bind(null,"5f80"))},uTextarea:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-textarea/u-textarea")]).then(n.bind(null,"269f"))},uButton:function(){return Promise.all([n.e("common/vendor"),n.e("uni_modules/uview-ui/components/u-button/u-button")]).then(n.bind(null,"11af"))}},a=function(){var e=this,t=e.$createElement;e._self._c;e._isMounted||(e.e0=function(t){e.showEventPicker=!1},e.e1=function(t){e.showEventPicker=!1},e.e2=function(t){e.showEventPicker=!0},e.e3=function(t){e.showPaymentPicker=!1},e.e4=function(t){e.showPaymentPicker=!1},e.e5=function(t){e.showPaymentPicker=!0},e.e6=function(t){e.showWeChatPicker=!1},e.e7=function(t){e.showWeChatPicker=!1},e.e8=function(t){e.showWeChatPicker=!0},e.e9=function(t){!e.loading&&e.handleFormSubmit()})},o=[]},a2de:function(e,t,n){"use strict";var r=n("7445"),a=n.n(r);a.a},e61b:function(e,t,n){"use strict";(function(e,t){var r=n("47a9");n("2fec");r(n("3240"));var a=r(n("0ac1"));e.__webpack_require_UNI_MP_PLUGIN__=n,t(a.default)}).call(this,n("3223")["default"],n("df3c")["createPage"])}},[["e61b","common/runtime","common/vendor"]]]);
3 3 \ No newline at end of file
... ...
绿纤uni-app/unpackage/dist/build/mp-weixin/pages/expansion/expansion.wxml
1   -<view class="container data-v-6122c20a"><view class="form-card data-v-6122c20a"><view class="form-content data-v-6122c20a"><u-form vue-id="9be4b680-1" model="{{formData}}" rules="{{rules}}" labelPosition="top" labelWidth="200" data-ref="form" data-event-opts="{{[['^submit',[['handleFormSubmit']]]]}}" bind:submit="__e" class="data-v-6122c20a vue-ref" bind:__l="__l" vue-slots="{{['default']}}"><u-form-item vue-id="{{('9be4b680-2')+','+('9be4b680-1')}}" label="拓客活动" prop="eventId" required="{{true}}" class="data-v-6122c20a" bind:__l="__l" vue-slots="{{['default']}}"><u-picker vue-id="{{('9be4b680-3')+','+('9be4b680-2')}}" show="{{showEventPicker}}" columns="{{[eventList]}}" data-event-opts="{{[['^confirm',[['onEventConfirm']]],['^cancel',[['e0']]],['^close',[['e1']]]]}}" bind:confirm="__e" bind:cancel="__e" bind:close="__e" class="data-v-6122c20a" bind:__l="__l"></u-picker><view data-event-opts="{{[['tap',[['e2',['$event']]]]]}}" class="picker-trigger data-v-6122c20a" bindtap="__e"><text class="picker-text data-v-6122c20a">{{selectedEventName||'请选择拓客活动'}}</text><u-icon vue-id="{{('9be4b680-4')+','+('9be4b680-2')}}" name="arrow-down" size="16" color="#6a9c6a" class="data-v-6122c20a" bind:__l="__l"></u-icon></view></u-form-item><u-form-item vue-id="{{('9be4b680-5')+','+('9be4b680-1')}}" label="顾客姓名" prop="customerName" required="{{true}}" class="data-v-6122c20a" bind:__l="__l" vue-slots="{{['default']}}"><u-input bind:input="__e" vue-id="{{('9be4b680-6')+','+('9be4b680-5')}}" placeholder="请输入顾客姓名" border="none" customStyle="{{inputStyle}}" value="{{formData.customerName}}" data-event-opts="{{[['^input',[['__set_model',['$0','customerName','$event',[]],['formData']]]]]}}" class="data-v-6122c20a" bind:__l="__l"></u-input></u-form-item><u-form-item vue-id="{{('9be4b680-7')+','+('9be4b680-1')}}" label="电话号码" prop="customerPhone" required="{{true}}" class="data-v-6122c20a" bind:__l="__l" vue-slots="{{['default']}}"><u-input bind:input="__e" vue-id="{{('9be4b680-8')+','+('9be4b680-7')}}" placeholder="请输入电话号码" type="number" border="none" customStyle="{{inputStyle}}" value="{{formData.customerPhone}}" data-event-opts="{{[['^input',[['__set_model',['$0','customerPhone','$event',[]],['formData']]]]]}}" class="data-v-6122c20a" bind:__l="__l"></u-input></u-form-item><u-form-item vue-id="{{('9be4b680-9')+','+('9be4b680-1')}}" label="支付方式" prop="paymentMethod" required="{{true}}" class="data-v-6122c20a" bind:__l="__l" vue-slots="{{['default']}}"><u-picker vue-id="{{('9be4b680-10')+','+('9be4b680-9')}}" show="{{showPaymentPicker}}" columns="{{[paymentOptions]}}" data-event-opts="{{[['^confirm',[['onPaymentConfirm']]],['^cancel',[['e3']]],['^close',[['e4']]]]}}" bind:confirm="__e" bind:cancel="__e" bind:close="__e" class="data-v-6122c20a" bind:__l="__l"></u-picker><view data-event-opts="{{[['tap',[['e5',['$event']]]]]}}" class="picker-trigger data-v-6122c20a" bindtap="__e"><text class="picker-text data-v-6122c20a">{{formData.paymentMethod||'请选择支付方式'}}</text><u-icon vue-id="{{('9be4b680-11')+','+('9be4b680-9')}}" name="arrow-down" size="16" color="#6a9c6a" class="data-v-6122c20a" bind:__l="__l"></u-icon></view></u-form-item><u-form-item vue-id="{{('9be4b680-12')+','+('9be4b680-1')}}" label="是否加微信" prop="isAddWeChat" required="{{true}}" class="data-v-6122c20a" bind:__l="__l" vue-slots="{{['default']}}"><u-picker vue-id="{{('9be4b680-13')+','+('9be4b680-12')}}" show="{{showWeChatPicker}}" columns="{{[weChatOptions]}}" data-event-opts="{{[['^confirm',[['onWeChatConfirm']]],['^cancel',[['e6']]],['^close',[['e7']]]]}}" bind:confirm="__e" bind:cancel="__e" bind:close="__e" class="data-v-6122c20a" bind:__l="__l"></u-picker><view data-event-opts="{{[['tap',[['e8',['$event']]]]]}}" class="picker-trigger data-v-6122c20a" bindtap="__e"><text class="picker-text data-v-6122c20a">{{formData.isAddWeChat||'请选择是否加微信'}}</text><u-icon vue-id="{{('9be4b680-14')+','+('9be4b680-12')}}" name="arrow-down" size="16" color="#6a9c6a" class="data-v-6122c20a" bind:__l="__l"></u-icon></view></u-form-item><u-form-item vue-id="{{('9be4b680-15')+','+('9be4b680-1')}}" label="备注" prop="remarks" class="data-v-6122c20a" bind:__l="__l" vue-slots="{{['default']}}"><u-textarea bind:input="__e" vue-id="{{('9be4b680-16')+','+('9be4b680-15')}}" placeholder="请输入备注信息" border="none" customStyle="{{textareaStyle}}" height="{{120}}" value="{{formData.remarks}}" data-event-opts="{{[['^input',[['__set_model',['$0','remarks','$event',[]],['formData']]]]]}}" class="data-v-6122c20a" bind:__l="__l"></u-textarea></u-form-item><u-button vue-id="{{('9be4b680-17')+','+('9be4b680-1')}}" type="primary" loading="{{loading}}" disabled="{{loading}}" customStyle="{{buttonStyle}}" data-event-opts="{{[['^click',[['e9']]]]}}" bind:click="__e" class="data-v-6122c20a" bind:__l="__l" vue-slots="{{['default']}}">{{''+(loading?'提交中...':'保存')+''}}</u-button></u-form></view></view></view>
2 1 \ No newline at end of file
  2 +<view class="container data-v-05d8df92"><view class="form-card data-v-05d8df92"><view class="form-content data-v-05d8df92"><u-form vue-id="9be4b680-1" model="{{formData}}" rules="{{rules}}" labelPosition="top" labelWidth="200" data-ref="form" data-event-opts="{{[['^submit',[['handleFormSubmit']]]]}}" bind:submit="__e" class="data-v-05d8df92 vue-ref" bind:__l="__l" vue-slots="{{['default']}}"><u-form-item vue-id="{{('9be4b680-2')+','+('9be4b680-1')}}" label="拓客活动" prop="eventId" required="{{true}}" class="data-v-05d8df92" bind:__l="__l" vue-slots="{{['default']}}"><u-picker vue-id="{{('9be4b680-3')+','+('9be4b680-2')}}" show="{{showEventPicker}}" columns="{{[eventList]}}" data-event-opts="{{[['^confirm',[['onEventConfirm']]],['^cancel',[['e0']]],['^close',[['e1']]]]}}" bind:confirm="__e" bind:cancel="__e" bind:close="__e" class="data-v-05d8df92" bind:__l="__l"></u-picker><view data-event-opts="{{[['tap',[['e2',['$event']]]]]}}" class="picker-trigger data-v-05d8df92" bindtap="__e"><text class="picker-text data-v-05d8df92">{{selectedEventName||'请选择拓客活动'}}</text><u-icon vue-id="{{('9be4b680-4')+','+('9be4b680-2')}}" name="arrow-down" size="16" color="#6a9c6a" class="data-v-05d8df92" bind:__l="__l"></u-icon></view></u-form-item><u-form-item vue-id="{{('9be4b680-5')+','+('9be4b680-1')}}" label="顾客姓名" prop="customerName" required="{{true}}" class="data-v-05d8df92" bind:__l="__l" vue-slots="{{['default']}}"><u-input bind:input="__e" vue-id="{{('9be4b680-6')+','+('9be4b680-5')}}" placeholder="请输入顾客姓名" border="none" customStyle="{{inputStyle}}" value="{{formData.customerName}}" data-event-opts="{{[['^input',[['__set_model',['$0','customerName','$event',[]],['formData']]]]]}}" class="data-v-05d8df92" bind:__l="__l"></u-input></u-form-item><u-form-item vue-id="{{('9be4b680-7')+','+('9be4b680-1')}}" label="电话号码" prop="customerPhone" required="{{true}}" class="data-v-05d8df92" bind:__l="__l" vue-slots="{{['default']}}"><u-input bind:input="__e" vue-id="{{('9be4b680-8')+','+('9be4b680-7')}}" placeholder="请输入电话号码" type="number" border="none" customStyle="{{inputStyle}}" value="{{formData.customerPhone}}" data-event-opts="{{[['^input',[['__set_model',['$0','customerPhone','$event',[]],['formData']]]]]}}" class="data-v-05d8df92" bind:__l="__l"></u-input></u-form-item><u-form-item vue-id="{{('9be4b680-9')+','+('9be4b680-1')}}" label="支付方式" prop="paymentMethod" required="{{true}}" class="data-v-05d8df92" bind:__l="__l" vue-slots="{{['default']}}"><u-picker vue-id="{{('9be4b680-10')+','+('9be4b680-9')}}" show="{{showPaymentPicker}}" columns="{{[paymentOptions]}}" data-event-opts="{{[['^confirm',[['onPaymentConfirm']]],['^cancel',[['e3']]],['^close',[['e4']]]]}}" bind:confirm="__e" bind:cancel="__e" bind:close="__e" class="data-v-05d8df92" bind:__l="__l"></u-picker><view data-event-opts="{{[['tap',[['e5',['$event']]]]]}}" class="picker-trigger data-v-05d8df92" bindtap="__e"><text class="picker-text data-v-05d8df92">{{formData.paymentMethod||'请选择支付方式'}}</text><u-icon vue-id="{{('9be4b680-11')+','+('9be4b680-9')}}" name="arrow-down" size="16" color="#6a9c6a" class="data-v-05d8df92" bind:__l="__l"></u-icon></view></u-form-item><u-form-item vue-id="{{('9be4b680-12')+','+('9be4b680-1')}}" label="是否加微信" prop="isAddWeChat" required="{{true}}" class="data-v-05d8df92" bind:__l="__l" vue-slots="{{['default']}}"><u-picker vue-id="{{('9be4b680-13')+','+('9be4b680-12')}}" show="{{showWeChatPicker}}" columns="{{[weChatOptions]}}" data-event-opts="{{[['^confirm',[['onWeChatConfirm']]],['^cancel',[['e6']]],['^close',[['e7']]]]}}" bind:confirm="__e" bind:cancel="__e" bind:close="__e" class="data-v-05d8df92" bind:__l="__l"></u-picker><view data-event-opts="{{[['tap',[['e8',['$event']]]]]}}" class="picker-trigger data-v-05d8df92" bindtap="__e"><text class="picker-text data-v-05d8df92">{{formData.isAddWeChat||'请选择是否加微信'}}</text><u-icon vue-id="{{('9be4b680-14')+','+('9be4b680-12')}}" name="arrow-down" size="16" color="#6a9c6a" class="data-v-05d8df92" bind:__l="__l"></u-icon></view></u-form-item><u-form-item vue-id="{{('9be4b680-15')+','+('9be4b680-1')}}" label="备注" prop="remarks" class="data-v-05d8df92" bind:__l="__l" vue-slots="{{['default']}}"><u-textarea bind:input="__e" vue-id="{{('9be4b680-16')+','+('9be4b680-15')}}" placeholder="请输入备注信息" border="none" customStyle="{{textareaStyle}}" height="{{120}}" value="{{formData.remarks}}" data-event-opts="{{[['^input',[['__set_model',['$0','remarks','$event',[]],['formData']]]]]}}" class="data-v-05d8df92" bind:__l="__l"></u-textarea></u-form-item><u-button vue-id="{{('9be4b680-17')+','+('9be4b680-1')}}" type="primary" loading="{{loading}}" disabled="{{loading}}" customStyle="{{buttonStyle}}" data-event-opts="{{[['^click',[['e9']]]]}}" bind:click="__e" class="data-v-05d8df92" bind:__l="__l" vue-slots="{{['default']}}">{{''+(loading?'提交中...':'保存')+''}}</u-button></u-form></view></view></view>
3 3 \ No newline at end of file
... ...
绿纤uni-app/unpackage/dist/build/mp-weixin/pages/expansion/expansion.wxss
1   -.container.data-v-6122c20a{font-family:PingFang SC,Microsoft YaHei,Arial,sans-serif;margin:0;height:100vh;background:linear-gradient(135deg,#e8f5e9,#b2dfdb);padding:40rpx;width:100%;box-sizing:border-box;overflow-y:scroll}.page-title.data-v-6122c20a{text-align:center;color:#388e3c;margin-bottom:36rpx;letter-spacing:4rpx;font-size:36rpx;font-weight:700}.form-card.data-v-6122c20a{background:#fff;border-radius:40rpx;box-shadow:0 16rpx 64rpx 0 rgba(76,175,80,.15);border:2rpx solid #e8f5e9;overflow:hidden}.form-content.data-v-6122c20a{padding:64rpx;box-sizing:border-box}.form-group.data-v-6122c20a{margin-bottom:48rpx}.form-group.data-v-6122c20a:last-child{margin-bottom:0}label.data-v-6122c20a{display:block;margin-bottom:20rpx;font-weight:600;color:#2e7d32;letter-spacing:1rpx;font-size:28rpx}.input-wrapper.data-v-6122c20a{position:relative}.input-icon.data-v-6122c20a{position:absolute;left:24rpx;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);width:40rpx;height:40rpx;color:#6a9c6a}input.data-v-6122c20a,
2   -textarea.data-v-6122c20a,
3   -._select.data-v-6122c20a,
4   -.form-select.data-v-6122c20a{width:100%;padding:24rpx;border:3rpx solid #c8e6c9;border-radius:20rpx;font-size:28rpx;transition:all .2s ease;background:#f9fff9;color:#2e7d32;font-family:inherit;box-sizing:border-box}textarea.data-v-6122c20a{min-height:200rpx;resize:vertical;padding:32rpx;line-height:1.5}._select.data-v-6122c20a{cursor:pointer;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236a9c6a' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E");background-position:right 24rpx center;background-repeat:no-repeat;background-size:32rpx;padding-right:80rpx;appearance:none;-webkit-appearance:none;-moz-appearance:none}._select.data-v-6122c20a:focus{outline:none;border-color:#43a047;box-shadow:0 0 0 6rpx rgba(76,175,80,.1);background-color:#fff}input.data-v-6122c20a:focus,
5   -textarea.data-v-6122c20a:focus,
6   -._select.data-v-6122c20a:focus,
7   -.form-select.data-v-6122c20a:focus{outline:none;border-color:#43a047;box-shadow:0 0 0 6rpx rgba(76,175,80,.1);background:#fff}input.data-v-6122c20a:disabled{background:#f5f5f5;color:#666;cursor:not-allowed}.btn.data-v-6122c20a{width:100%;padding:32rpx 40rpx;border:none;border-radius:24rpx;font-size:32rpx;font-weight:600;cursor:pointer;transition:all .3s ease;letter-spacing:2rpx;background:linear-gradient(135deg,#43e97b,#38f9d7);color:#fff;box-shadow:0 8rpx 32rpx rgba(67,233,123,.3);margin-top:64rpx}.btn.data-v-6122c20a:hover{box-shadow:0 12rpx 48rpx rgba(67,233,123,.4);-webkit-transform:translateY(-4rpx);transform:translateY(-4rpx)}.btn.data-v-6122c20a:active{-webkit-transform:translateY(0);transform:translateY(0)}.picker-trigger.data-v-6122c20a{display:flex;align-items:center;justify-content:space-between;width:100%;padding:24rpx;background:#f9fff9;border:3rpx solid #c8e6c9;border-radius:20rpx;font-size:28rpx;color:#2e7d32;transition:all .2s ease;box-sizing:border-box}.picker-trigger.data-v-6122c20a:active{border-color:#43a047;box-shadow:0 0 0 6rpx rgba(76,175,80,.1);background:#fff}.picker-text.data-v-6122c20a{flex:1;color:#2e7d32}
8 1 \ No newline at end of file
  2 +.container.data-v-05d8df92{font-family:PingFang SC,Microsoft YaHei,Arial,sans-serif;margin:0;height:100vh;background:linear-gradient(135deg,#e8f5e9,#b2dfdb);padding:40rpx;width:100%;box-sizing:border-box;overflow-y:scroll}.page-title.data-v-05d8df92{text-align:center;color:#388e3c;margin-bottom:36rpx;letter-spacing:4rpx;font-size:36rpx;font-weight:700}.form-card.data-v-05d8df92{background:#fff;border-radius:40rpx;box-shadow:0 16rpx 64rpx 0 rgba(76,175,80,.15);border:2rpx solid #e8f5e9;overflow:hidden}.form-content.data-v-05d8df92{padding:64rpx;box-sizing:border-box}.form-group.data-v-05d8df92{margin-bottom:48rpx}.form-group.data-v-05d8df92:last-child{margin-bottom:0}label.data-v-05d8df92{display:block;margin-bottom:20rpx;font-weight:600;color:#2e7d32;letter-spacing:1rpx;font-size:28rpx}.input-wrapper.data-v-05d8df92{position:relative}.input-icon.data-v-05d8df92{position:absolute;left:24rpx;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);width:40rpx;height:40rpx;color:#6a9c6a}input.data-v-05d8df92,
  3 +textarea.data-v-05d8df92,
  4 +._select.data-v-05d8df92,
  5 +.form-select.data-v-05d8df92{width:100%;padding:24rpx;border:3rpx solid #c8e6c9;border-radius:20rpx;font-size:28rpx;transition:all .2s ease;background:#f9fff9;color:#2e7d32;font-family:inherit;box-sizing:border-box}textarea.data-v-05d8df92{min-height:200rpx;resize:vertical;padding:32rpx;line-height:1.5}._select.data-v-05d8df92{cursor:pointer;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236a9c6a' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E");background-position:right 24rpx center;background-repeat:no-repeat;background-size:32rpx;padding-right:80rpx;appearance:none;-webkit-appearance:none;-moz-appearance:none}._select.data-v-05d8df92:focus{outline:none;border-color:#43a047;box-shadow:0 0 0 6rpx rgba(76,175,80,.1);background-color:#fff}input.data-v-05d8df92:focus,
  6 +textarea.data-v-05d8df92:focus,
  7 +._select.data-v-05d8df92:focus,
  8 +.form-select.data-v-05d8df92:focus{outline:none;border-color:#43a047;box-shadow:0 0 0 6rpx rgba(76,175,80,.1);background:#fff}input.data-v-05d8df92:disabled{background:#f5f5f5;color:#666;cursor:not-allowed}.btn.data-v-05d8df92{width:100%;padding:32rpx 40rpx;border:none;border-radius:24rpx;font-size:32rpx;font-weight:600;cursor:pointer;transition:all .3s ease;letter-spacing:2rpx;background:linear-gradient(135deg,#43e97b,#38f9d7);color:#fff;box-shadow:0 8rpx 32rpx rgba(67,233,123,.3);margin-top:64rpx}.btn.data-v-05d8df92:hover{box-shadow:0 12rpx 48rpx rgba(67,233,123,.4);-webkit-transform:translateY(-4rpx);transform:translateY(-4rpx)}.btn.data-v-05d8df92:active{-webkit-transform:translateY(0);transform:translateY(0)}.picker-trigger.data-v-05d8df92{display:flex;align-items:center;justify-content:space-between;width:100%;padding:24rpx;background:#f9fff9;border:3rpx solid #c8e6c9;border-radius:20rpx;font-size:28rpx;color:#2e7d32;transition:all .2s ease;box-sizing:border-box}.picker-trigger.data-v-05d8df92:active{border-color:#43a047;box-shadow:0 0 0 6rpx rgba(76,175,80,.1);background:#fff}.picker-text.data-v-05d8df92{flex:1;color:#2e7d32}
9 9 \ No newline at end of file
... ...
绿纤uni-app/unpackage/dist/build/mp-weixin/project.private.config.json
1 1 {
2 2 "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
3   - "libVersion": "3.14.1"
  3 + "libVersion": "3.14.3"
4 4 }
5 5 \ No newline at end of file
... ...
绿纤uni-app/unpackage/dist/build/mp-weixin/uni_modules/uview-ui/components/u-gap/u-gap.json
1 1 {
2   - "usingComponents": {},
3   - "component": true
  2 + "component": true,
  3 + "usingComponents": {}
4 4 }
5 5 \ No newline at end of file
... ...
绿纤uni-app/unpackage/dist/build/mp-weixin/uni_modules/uview-ui/components/u-toolbar/u-toolbar.json
1 1 {
2   - "usingComponents": {},
3   - "component": true
  2 + "component": true,
  3 + "usingComponents": {}
4 4 }
5 5 \ No newline at end of file
... ...
绿纤uni-app/unpackage/dist/dev/mp-weixin/pages/expansion/expansion.js
... ... @@ -341,11 +341,19 @@ var _default = {
341 341 customerPhone: [{
342 342 required: true,
343 343 message: '请输入电话号码',
344   - trigger: 'blur'
  344 + trigger: ['blur', 'change']
345 345 }, {
346   - pattern: /^1[3-9]\d{9}$/,
347   - message: '请输入正确的手机号码',
348   - trigger: 'blur'
  346 + validator: function validator(rule, value, callback) {
  347 + var phone = String(value || '').trim();
  348 + if (!phone) {
  349 + callback(new Error('请输入电话号码'));
  350 + } else if (!/^1[3-9]\d{9}$/.test(phone)) {
  351 + callback(new Error('请输入正确的手机号码'));
  352 + } else {
  353 + callback();
  354 + }
  355 + },
  356 + trigger: ['blur', 'change']
349 357 }],
350 358 paymentMethod: [{
351 359 required: true,
... ... @@ -571,36 +579,48 @@ var _default = {
571 579 handleFormSubmit: function handleFormSubmit() {
572 580 var _this4 = this;
573 581 return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4() {
574   - var formData;
  582 + var errors, formData;
575 583 return _regenerator.default.wrap(function _callee4$(_context4) {
576 584 while (1) {
577 585 switch (_context4.prev = _context4.next) {
578 586 case 0:
579   - console.error('【【【【【【');
580   - _context4.prev = 1;
581   - _context4.next = 4;
  587 + _context4.prev = 0;
  588 + _context4.next = 3;
582 589 return _this4.$refs.form.validate();
583   - case 4:
  590 + case 3:
  591 + _context4.next = 5;
  592 + return _this4.validateForm();
  593 + case 5:
  594 + errors = _context4.sent;
  595 + if (!(errors && errors.length > 0)) {
  596 + _context4.next = 9;
  597 + break;
  598 + }
  599 + uni.showToast({
  600 + title: errors[0],
  601 + icon: 'none',
  602 + duration: 2000
  603 + });
  604 + return _context4.abrupt("return");
  605 + case 9:
584 606 // 收集表单数据
585 607 formData = _this4.collectFormData();
586 608 console.log('表单数据:', formData);
587   -
588   - // 提交数据
589   - _context4.next = 8;
590   - return _this4.submitExpansion(formData);
591   - case 8:
592 609 _context4.next = 13;
  610 + return _this4.submitExpansion(formData);
  611 + case 13:
  612 + _context4.next = 18;
593 613 break;
594   - case 10:
595   - _context4.prev = 10;
596   - _context4.t0 = _context4["catch"](1);
  614 + case 15:
  615 + _context4.prev = 15;
  616 + _context4.t0 = _context4["catch"](0);
597 617 console.log('表单验证失败:', _context4.t0);
598   - case 13:
  618 + case 18:
599 619 case "end":
600 620 return _context4.stop();
601 621 }
602 622 }
603   - }, _callee4, null, [[1, 10]]);
  623 + }, _callee4, null, [[0, 15]]);
604 624 }))();
605 625 },
606 626 // 提交扩展邀约数据
... ...
绿纤uni-app/unpackage/dist/dev/mp-weixin/project.private.config.json
... ... @@ -4,6 +4,20 @@
4 4 "miniprogram": {
5 5 "list": [
6 6 {
  7 + "name": "pages/expansion/expansion",
  8 + "pathName": "pages/expansion/expansion",
  9 + "query": "",
  10 + "launchMode": "default",
  11 + "scene": null
  12 + },
  13 + {
  14 + "name": "pages/addServiceLog/addServiceLog",
  15 + "pathName": "pages/addServiceLog/addServiceLog",
  16 + "query": "consumeId=800705717190264069&memberName=%25E5%2588%2598%25E7%258E%2589%25E5%2585%25B0",
  17 + "launchMode": "default",
  18 + "scene": null
  19 + },
  20 + {
7 21 "name": "pages/web/web",
8 22 "pathName": "pages/web/web",
9 23 "query": "url=https%253A%252F%252Ferp.lvqianmeiye.com%252Fhtml%252FdailyReport.html",
... ...