活动介绍
file-type

使用JavaScript绘制动态椭圆图形教程

ZIP文件

下载需积分: 50 | 808B | 更新于2024-10-21 | 38 浏览量 | 0 下载量 举报 收藏
download 立即下载
该代码的主要功能是通过使用JavaScript的p5.js库,来实现一个简单的动态图形效果。 具体来说,这段代码包含两个主要的函数:setup()和draw()。 首先,setup()函数是程序的初始化部分,它只运行一次。在这个函数中,我们调用了createCanvas(600,600)函数,用于创建一个宽度和高度都是600像素的画布。然后,调用了background(100)函数,用于设置画布的背景颜色,其中100代表灰度值。 接着,draw()函数是程序的主循环部分,它会不断重复执行。在这个函数中,我们调用了ellipse(mouseX, mouseY, 20, 20)函数,用于在画布上绘制一个椭圆。其中,mouseX和mouseY代表鼠标在画布上的位置,20代表椭圆的宽度和高度,都是20像素。 需要注意的是,这段代码中使用了p5.js库,这是一个开源的JavaScript库,用于简化图形,动画和Web交互的设计和创建。因此,要想运行这段代码,需要在HTML文件中引入p5.js库。 总的来说,这段代码展示了一个非常基础的p5.js应用,通过学习这段代码,我们可以了解到如何使用p5.js创建动态图形,如何处理鼠标事件,以及如何在网页中嵌入JavaScript代码。"

相关推荐

filetype

<template> <view class="login-container"> <view class="login-form"> <text class="login-title">用户登录</text> <input v-model="username" placeholder="用户名" class="login-input" placeholder-class="input-placeholder" /> <input v-model="password" type="password" placeholder="密码" class="login-input" placeholder-class="input-placeholder" /> <button class="login-button" :loading="loading" @click="handleLogin" >登录</button> </view> </view> </template> <script> import { ref } from 'vue' export default { setup() { const username = ref('') const password = ref('') const loading = ref(false) const handleLogin = async () => { // 验证输入 if (!username.value.trim()) { uni.showToast({ title: '请输入用户名', icon: 'none', duration: 2000 }) return } if (!password.value) { uni.showToast({ title: '请输入密码', icon: 'none', duration: 2000 }) return } loading.value = true try { // 兼容性请求 const response = await new Promise((resolve, reject) => { uni.request({ url: 'http://172.26.26.43/dev-api/login', method: 'POST', data: { username: username.value, password: password.value }, header: { 'Content-Type': 'application/json' }, timeout: 10000, success: (res) => resolve(res), fail: (err) => reject(err) }) }) // 处理响应 await handleResponse(response) } catch (error) { console.error('登录请求出错:', error) let errorMsg = '登录失败,请重试' if (error.errMsg) { errorMsg = `请求失败: ${error.errMsg}` } else if (error.message) { errorMsg = error.message } uni.showToast({ title: errorMsg, icon: 'none', duration: 2000 }) } finally { loading.value = false } } const handleResponse = async (response) => { // 检查HTTP状态码 if (response.statusCode < 200 || response.statusCode >= 300) { throw new Error(`服务器错误: ${response.statusCode}`) } // 解析响应数据 const { data } = response if (!data) { throw new Error('服务器返回空数据') } const { code, message, token } = data if (code === 200) { // 存储token uni.setStorageSync('token', token) uni.showToast({ title: '登录成功!', icon: 'success', duration: 1000 }) // 登录成功后跳转 setTimeout(() => { uni.redirectTo({ url: '/pages/detail/detail?id=123' }) }, 1000) } else { throw new Error(`登录失败: ${message || '未知错误'}`) } } return { username, password, loading, handleLogin } } } </script> <style scoped> .login-container { display: flex; justify-content: center; align-items: center; min-height: 100vh; padding: 32rpx; background-color: #f5f5f5; } .login-form { width: 100%; max-width: 600rpx; padding: 60rpx; background: #fff; border-radius: 16rpx; box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1); } .login-title { display: block; text-align: center; margin-bottom: 60rpx; font-size: 40rpx; font-weight: bold; color: #333; } .login-input { width: 100%; height: 88rpx; padding: 0 24rpx; margin-bottom: 40rpx; border: 2rpx solid #ddd; border-radius: 12rpx; font-size: 32rpx; box-sizing: border-box; } .input-placeholder { color: #999; font-size: 32rpx; } .login-button { width: 100%; height: 88rpx; line-height: 88rpx; background-color: #007aff; color: white; font-size: 32rpx; border-radius: 12rpx; margin-top: 20rpx; } .login-button:active { background-color: #0062cc; opacity: 0.9; } </style> 报错:09:16:45.721 应用【vue3-uni-mycp】已启动 09:17:07.568 跳转登录页成功 at pages/index/index.vue:16 09:17:17.980 Listener added for a 'DOMNodeInserted' mutation event. Support for this event type has been removed, and this event will no longer be fired. See https://chromestatus.com/feature/5083947249172480 for more information. at __uniappquill.js:7 09:17:22.990 [Vue warn]: Unhandled error during execution of native event handler at <Operation__pageId=4__pagePath="pages/operation/operation"__pageQuery={"id":"11"} ...> 09:17:22.990 TypeError: _ctx.setRating is not a function 09:17:34.523 [Vue warn]: Unhandled error during execution of native event handler at <Operation__pageId=4__pagePath="pages/operation/operation"__pageQuery={"id":"11"} ...> 09:17:34.523 TypeError: _ctx.setScore26 is not a function 09:17:36.069 [Vue warn]: Unhandled error during execution of native event handler at <Operation__pageId=4__pagePath="pages/operation/operation"__pageQuery={"id":"11"} ...> 09:17:36.070 TypeError: _ctx.setRating is not a function 09:17:37.818 App Hide at App.vue:10

filetype

<template>
{{ alertMessage }}

投票人:{{ voterName }}

身份证:{{ formattedIdCard }}

经理投票

{{ votes[17] }} / 5

厂长投票

{{ votes[18] }} / 5

副厂长投票

{{ votes[19] }} / 15

总票数

{{ totalVotes }} / 25

{{ voter.name }}

ID: {{ voter.id }}

<button @click="castVote(voter, 17)" :disabled="!canVote(voter, 17)" :class="{ 'selected': voter.vote === 17, 'disabled': !canVote(voter, 17) }" > 经理 </button> <button @click="castVote(voter, 18)" :disabled="!canVote(voter, 18)" :class="{ 'selected': voter.vote === 18, 'disabled': !canVote(voter, 18) }" > 厂长 </button> <button @click="castVote(voter, 19)" :disabled="!canVote(voter, 19)" :class="{ 'selected': voter.vote === 19, 'disabled': !canVote(voter, 19) }" > 副厂长 </button>
<button @click="submitVotes" :disabled="!hasSelectedCandidates || isSubmitting">提交投票</button> <button @click="resetVotes" :disabled="isSubmitting">重置投票</button>
</template> <script setup> import { ref, reactive, computed } from 'vue'; import { useRoute, useRouter} from 'vue-router'; import { onMounted } from 'vue'; const route = useRoute(); const router = useRouter() const voters = ref([]); //候选人 // 使用props接收参数 const props = defineProps({ queryParams: { type: Object, default: () => ({}) } }) // 使用计算属性 const activeSurvey = ref({ surveyId: route.params.surveyId,//调查令 qydcl: route.params.qydcl, dclx: route.query.dclx, tffs: route.query.tffs, bt: route.query.bt }); onMounted(async() => { // 确保路由完全解析完成 await router.isReady() // console.log('当前路由对象:', route) // console.log('路径参数:', route.params) // 获取params参数 // console.log('查询参数:', route.query) // 获取query参数 }); // 添加消息提示状态 const alertMessage = ref(''); const showAlert = ref(false); const alertType = ref(''); // 'success' 或 'error' // 安全序列化函数 function safeStringify(obj) { const seen = new WeakSet(); return JSON.stringify(obj, (key, value) => { if (typeof value === "object" && value !== null) { if (seen.has(value)) { // 检测到循环引用,返回占位符或跳过 return "[Circular Reference Removed]"; } seen.add(value); } return value; }); } // onMounted生命周期钩子 onMounted(async () => { // 从sessionStorage获取投票人信息并立即清除 const voterInfo = sessionStorage.getItem('voterInfo'); if (voterInfo) { const { name, idCard } = JSON.parse(voterInfo); voterName.value = name; voterIdCard.value = idCard; sessionStorage.removeItem('voterInfo'); } // 获取投票详情 const route = useRoute(); // 加载候选人数据 voters.value = await fetchCandidates(); }); // 添加用于存储投票人信息的变量 const voterName = ref(''); const voterIdCard = ref(''); // 格式化身份证显示(安全脱敏) const formattedIdCard = computed(() => { if (!voterIdCard.value) return ''; // 显示前6位和后4位,中间用*代替 return voterIdCard.value.substring(0, 6) + '******' + voterIdCard.value.substring(voterIdCard.value.length - 4); }); onMounted(() => { // 从sessionStorage获取数据并立即清除 const voterInfo = sessionStorage.getItem('voterInfo'); if (voterInfo) { const { name, idCard } = JSON.parse(voterInfo); voterName.value = name; voterIdCard.value = idCard; // 关键:立即清除存储防止数据残留 sessionStorage.removeItem('voterInfo'); } }); //获取候选人明细 const fetchCandidates = async () => { try { const response = await fetch('/api/wechat/getInvestigate', { method: 'POST', body: JSON.stringify({ id: '9', dcl: '123' }) }); // console.log('API响应:', response); const result = await response.json(); if (!result || !result.root) throw new Error('无效API响应'); // 提取候选人数据 const candidateArray = []; let idCounter = 1; // 自增计数器,名称序号 result.root.forEach(rootItem => { if (!rootItem.childEntList) return; rootItem.childEntList.forEach(candidate => { if (!candidate.dcbt || !candidate.dcxbt) return; candidateArray.push({ name: candidate.dcxbt, vote: null, id:idCounter++, tmlx: candidate.tmlx, // 投票类型 mainId: candidate.mainId, // 主ID dcbt: candidate.dcbt, // 投票标题 }); }); }); // console.log('候选人数据:', candidateArray); return candidateArray; } catch (error) { console.error('获取候选人失败:', error); return []; // 返回空数组保持安全 } }; // 新增计算属性 - 获取已选择的候选人 const selectedCandidates = computed(() => { return voters.value .filter(v => v.vote) // 只过滤已投票的 .map(v => ({ mainId: v.mainId, // 候选人原始ID voteType: v.vote, // 投票类型 voteValue: v.vote, name: v.name, // 候选人姓名 tmlx: v.tmlx, // 原始类型 dcbt: v.dcbt // 投票标题 })); }); // 检查是否有选择的候选人 const hasSelectedCandidates = computed(() => { return selectedCandidates.value.length > 0; }); //获取并保存第一个候选人的mainId const mainIdToSubmit = ref(null); const firstCandidateMainId = computed(() => { return selectedCandidates.value[0]?.mainId || null; }); // 投票统计 const votes = reactive({ 17: 0, 18: 0, 19: 0 }); // 计算总票数 const totalVotes = computed(() => { return votes[17] + votes[18] + votes[19]; }); // 检查投票资格 const canVote = (voter, type) => { // 当前投票类型限额配置 const limits = { 17: 5, 18: 5, 19: 15 }; // 情况1:用户取消当前选择的类型(总是允许) if (voter.vote === type) return true; // 情况2:用户切换投票类型 if (voter.vote) { // 检查新类型是否达到限额 if (votes[type] >= limits[type]) return false; } // 情况3:用户首次投票 else { // 检查新类型是否达到限额和总票数 if (votes[type] >= limits[type] || totalVotes.value >= 25) return false; } return true; }; // 投票方法 const castVote = (voter, voteValue) => { // 如果已投票且点击相同类型,取消投票 if (voter.vote === voteValue) { voter.vote = null; votes[voteValue]--; return; } // 如果之前有投票,先取消 if (voter.vote !== null) { votes[voter.vote]--; } // 投新票 voter.vote = voteValue; votes[voteValue]++; }; //投票人信息 // 添加投票人信息数据模型 const voterInfo = reactive({ name: '', idNumber: '' }); // 提交投票 // 防止重复提交 const isSubmitting = ref(false); // 提交投票到API const submitVotes = async () => { // console.log("dclx", activeSurvey.value.dclx); // 添加防御性检查 if (!hasSelectedCandidates.value) { showMessage('请先选择候选人', 'error'); return; } // 防止重复提交 if (isSubmitting.value) return; isSubmitting.value = true; // 设置mainId值 mainIdToSubmit.value = firstCandidateMainId.value; try { const mainData = [ { fieldName: "bt", fieldValue: activeSurvey.value.bt }, { fieldName: "tprxm", fieldValue: voterName.value }, { fieldName: "tprsfz", fieldValue: voterIdCard.value }, { fieldName: "mb", fieldValue: mainIdToSubmit.value }, { fieldName: "dclx", fieldValue: activeSurvey.value.dclx }, { fieldName: "tffs", fieldValue: activeSurvey.value.tffs } ]; const workflowRequestTableRecords = selectedCandidates.value.map(candidate => ({ workflowRequestTableFields: [ { fieldName: "dcbt", fieldValue: candidate.dcbt }, { fieldName: "tmlx", fieldValue: candidate.tmlx }, { fieldName: "dcxx", fieldValue: candidate.vote === 'A' ? 17 : candidate.vote === 'B' ? 18 : 19 } ] })); // console.log('提交数据:', { // mainData, // workflowRequestTableRecords // }); const requestBody = { requestName: activeSurvey.value.bt, // 投票标题 workflowId: 118, // 固定工作流ID mainData, workflowRequestTableRecords }; // 发送POST请求 const response = await fetch('/api/wechat/addInvestigateWorkflow1', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: safeStringify(requestBody)// 使用安全序列化,避免重复引用 }); const result = await response.json(); // 根据 api_status 判断 if (result.api_status === true) { // 成功处理 const successMsg = result.msg || '投票提交成功!'; showMessage('投票提交成功!', 'success'); // 存储已投票标识 localStorage.setItem('voted_' + voterIdCard.value, 'true'); } else { // 处理错误情况 if (result.msg === '你已提交或不满足提交条件') { showMessage(result.msg, 'error'); // 特殊处理:用户已投票,存储标识防止重复提交 localStorage.setItem('voted_' + voterIdCard.value, 'true'); } else { // 其他错误情况 const errorMsg = result.msg ? `提交失败: ${result.msg}` : '未知错误,请稍后重试'; showMessage(errorMsg, 'error'); } } } catch (error) { console.error('网络请求失败:', error); showMessage('网络错误,请检查连接后重试', 'error'); } finally { isSubmitting.value = false; } }; // 重置投票 const resetVotes = () => { if (confirm('确定要重置所有投票吗?')) { voters.value.forEach(voter => { voter.vote = null; }); votes.A = 0; votes.B = 0; votes.C = 0; voterInfo.name = ''; voterInfo.idNumber = ''; } }; //后台响应信息showMessage const showMessage = (message, type = 'error') => { // 更新消息提示状态 alertMessage.value = message; showAlert.value = true; alertType.value = type; // 错误提示停留3秒,成功提示停留2秒 const timeout = type === 'error' ? 3000 : 2000; setTimeout(() => { showAlert.value = false; }, timeout); }; </script> <style scoped> /* 移动端垂直布局 */ @media (max-width: 480px) { .input-group { flex-direction: column; } } /* 平板/桌面端水平布局 */ @media (min-width: 768px) { .input-group { flex-direction: row; } } /* 消息提示样式 */ .alert { padding: 15px; margin-bottom: 20px; border-radius: 4px; text-align: center; position: fixed; top: 20px; left: 50%; transform: translateX(-50%); z-index: 1000; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); min-width: 300px; opacity: 0.95; } .alert.error { background-color: #ffebee; color: #b71c1c; border: 1px solid #ffcdd2; } .alert.success { background-color: #e8f5e9; color: #1b5e20; border: 1px solid #c8e6c9; } .vote-container { max-width: 1200px; margin: 0 auto; padding: 20px; } .stats { display: flex; justify-content: space-between; margin-bottom: 30px; background: #f5f7fa; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .stat { flex: 1; text-align: center; padding: 0 15px; } .progress { height: 20px; background: #e0e0e0; border-radius: 10px; margin: 10px 0; overflow: hidden; } .progress-bar { height: 100%; background: #3498db; transition: width 0.3s; } .voters-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 20px; } .voter-card { background: white; border-radius: 8px; padding: 15px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); transition: transform 0.2s; } .voter-card:hover { transform: translateY(-5px); } .voter-id { color: #777; font-size: 0.9rem; margin-bottom: 15px; } .vote-options { display: flex; justify-content: space-between; } .vote-options button { flex: 1; margin: 0 5px; padding: 8px 0; border: none; border-radius: 4px; cursor: pointer; transition: all 0.2s; } .vote-options button:not(.selected):hover { opacity: 0.9; transform: scale(1.05); } .vote-options button:first-child { background: #ff6b6b; color: white; } .vote-options button:nth-child(2) { background: #4ecdc4; color: white; } .vote-options button:last-child { background: #ffd166; color: white; } .selected { border: 2px solid #2c3e50 !important; font-weight: bold; box-shadow: 0 0 2 rgba(61, 60, 60, 0.5); } .disabled { opacity: 0.5 !important; cursor: not-allowed !important; } .action-buttons { margin-top: 30px; display: flex; justify-content: center; gap: 20px; } .action-buttons button { padding: 12px 30px; border: none; border-radius: 6px; cursor: pointer; font-size: 1rem; font-weight: 600; transition: all 0.2s; } .action-buttons button:first-child { background: #3498db; color: white; } .action-buttons button:first-child:disabled { background: #bdc3c7; cursor: not-allowed; } .action-buttons button:last-child { background: #e74c3c; color: white; } .action-buttons button:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } </style>浏览器报错:Uncaught (in promise) TypeError: _ctx.canVote is not a function at eval (VM10454 Vote.vue:88:23) at renderList (runtime-core.esm-bundler.js:2904:1) at Proxy.render (VM10454 Vote.vue:82:552) at renderComponentRoot (runtime-core.esm-bundler.js:6501:1) at ReactiveEffect.componentUpdateFn [as fn] (runtime-core.esm-bundler.js:5397:1) at ReactiveEffect.run (reactivity.esm-bundler.js:225:1) at ReactiveEffect.runIfDirty (reactivity.esm-bundler.js:263:1) at callWithErrorHandling (runtime-core.esm-bundler.js:199:1) at flushJobs (runtime-core.esm-bundler.js:408:1) eval @ VM10454 Vote.vue:88 renderList @ runtime-core.esm-bundler.js:2904 render @ VM10454 Vote.vue:82 renderComponentRoot @ runtime-core.esm-bundler.js:6501 componentUpdateFn @ runtime-core.esm-bundler.js:5397 run @ reactivity.esm-bundler.js:225 runIfDirty @ reactivity.esm-bundler.js:263 callWithErrorHandling @ runtime-core.esm-bundler.js:199 flushJobs @ runtime-core.esm-bundler.js:408 Promise.then queueFlush @ runtime-core.esm-bundler.js:322 queueJob @ runtime-core.esm-bundler.js:317 effect.scheduler @ runtime-core.esm-bundler.js:5448 trigger @ reactivity.esm-bundler.js:253 endBatch @ reactivity.esm-bundler.js:311 notify @ reactivity.esm-bundler.js:597 trigger @ reactivity.esm-bundler.js:571 set value @ reactivity.esm-bundler.js:1448 eval @ VM10456 Vote.vue:94 await in eval eval @ runtime-core.esm-bundler.js:2815 callWithErrorHandling @ runtime-core.esm-bundler.js:199 callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206 hook.__weh.hook.__weh @ runtime-core.esm-bundler.js:2795 flushPostFlushCbs @ runtime-core.esm-bundler.js:385 flushJobs @ runtime-core.esm-bundler.js:427 Promise.then queueFlush @ runtime-core.esm-bundler.js:322 queueJob @ runtime-core.esm-bundler.js:317 reload @ runtime-core.esm-bundler.js:527 eval @ runtime-core.esm-bundler.js:561 eval @ Vote.vue:15 ./src/views/Vote.vue @ about.js:1355 __webpack_require__ @ app.js:308 _requireSelf @ app.js:599 apply @ app.js:1335 (匿名) @ app.js:839 internalApply @ app.js:837 (匿名) @ app.js:775 waitForBlockingPromises @ app.js:730 (匿名) @ app.js:773 Promise.then (匿名) @ app.js:772 Promise.then (匿名) @ app.js:753 Promise.then hotCheck @ app.js:744 check @ dev-server.js:15 eval @ dev-server.js:69 emit @ events.js:153 reloadApp @ reloadApp.js:38 ok @ index.js:227 eval @ socket.js:62 client.onmessage @ WebSocketClient.js:45

filetype

// 验证成功后跳转页面 // 创建路由参数对象 const routeParams = { name: name.value, // 姓名 idCard: idCard.value, // 身份证号 qydcl: JSON.stringify(activeSurvey.value.qydcl), dclx: JSON.stringify(activeSurvey.value.dxcl), // 调查类型 tffs: activeSurvey.value.tffs, bt: activeSurvey.value.bt, // 调查标题 surveyId: activeSurvey.value.id //调查ID }; console.log('路由参数:', routeParams); // 根据调查类型跳转不同页面 router.push({ name: activeSurvey.value.dclx === '0' ? 'about' : 'Vote', params: routeParams });这段代码输出了准确的信息,但是Vote页面却没有获取到Vote页面代码如下:<template>
{{ alertMessage }}

投票人:{{ voterName }}

身份证:{{ formattedIdCard }}

经理投票

{{ votes.A }} / 5

厂长投票

{{ votes.B }} / 5

副厂长投票

{{ votes.C }} / 15

总票数

{{ totalVotes }} / 25

{{ voter.name }}

ID: {{ voter.id }}

<button @click="castVote(voter, 'A')" :disabled="!canVote(voter, 'A')" :class="{ 'selected': voter.vote === 'A', 'disabled': !canVote(voter, 'A') }" > 经理 </button> <button @click="castVote(voter, 'B')" :disabled="!canVote(voter, 'B')" :class="{ 'selected': voter.vote === 'B', 'disabled': !canVote(voter, 'B') }" > 厂长 </button> <button @click="castVote(voter, 'C')" :disabled="!canVote(voter, 'C')" :class="{ 'selected': voter.vote === 'C', 'disabled': !canVote(voter, 'C') }" > 副厂长 </button>
<button @click="submitVotes" :disabled="!hasSelectedCandidates || isSubmitting">提交投票</button> <button @click="resetVotes" :disabled="isSubmitting">重置投票</button>
</template> <script setup> import { ref, reactive, computed } from 'vue'; import { useRoute} from 'vue-router'; import { onMounted } from 'vue'; const route = useRoute(); onMounted(() => { // 确保路由参数已加载 console.log("当前路由参数:", route.query.bt); }); const voters = ref([]); //候选人 const activeData = ref({ bt: route.query.bt, dclx: JSON.parse(route.query.dxcl || '[]'), qydcl: JSON.parse(route.query.qydcl || '{}'), tffs: route.query.tffs, id: route.query.surveyId, userName: route.query.name, userIdCard: route.query.idCard }); console.log("接收到的调查标题:", activeData.value.bt) // 添加消息提示状态 const alertMessage = ref(''); const showAlert = ref(false); const alertType = ref(''); // 'success' 或 'error' // 安全序列化函数 function safeStringify(obj) { const seen = new WeakSet(); return JSON.stringify(obj, (key, value) => { if (typeof value === "object" && value !== null) { if (seen.has(value)) { // 检测到循环引用,返回占位符或跳过 return "[Circular Reference Removed]"; } seen.add(value); } return value; }); } // onMounted生命周期钩子 onMounted(async () => { // 从sessionStorage获取投票人信息并立即清除 const voterInfo = sessionStorage.getItem('voterInfo'); if (voterInfo) { const { name, idCard } = JSON.parse(voterInfo); voterName.value = name; voterIdCard.value = idCard; sessionStorage.removeItem('voterInfo'); } // 获取投票详情 const route = useRoute(); // 加载候选人数据 voters.value = await fetchCandidates(); }); // 添加用于存储投票人信息的变量 const voterName = ref(''); const voterIdCard = ref(''); // 格式化身份证显示(安全脱敏) const formattedIdCard = computed(() => { if (!voterIdCard.value) return ''; // 显示前6位和后4位,中间用*代替 return voterIdCard.value.substring(0, 6) + '******' + voterIdCard.value.substring(voterIdCard.value.length - 4); }); onMounted(() => { // 从sessionStorage获取数据并立即清除 const voterInfo = sessionStorage.getItem('voterInfo'); if (voterInfo) { const { name, idCard } = JSON.parse(voterInfo); voterName.value = name; voterIdCard.value = idCard; // 关键:立即清除存储防止数据残留 sessionStorage.removeItem('voterInfo'); } }); //获取候选人明细 const fetchCandidates = async () => { try { const response = await fetch('/api/wechat/getInvestigate', { method: 'POST', body: JSON.stringify({ id: '9', dcl: '123' }) }); // console.log('API响应:', response); const result = await response.json(); if (!result || !result.root) throw new Error('无效API响应'); // 提取候选人数据 const candidateArray = []; let idCounter = 1; // 自增计数器,名称序号 result.root.forEach(rootItem => { if (!rootItem.childEntList) return; rootItem.childEntList.forEach(candidate => { if (!candidate.dcbt || !candidate.dcxbt) return; candidateArray.push({ originalid: candidate.dcbt, name: candidate.dcxbt, vote: null, id:idCounter++, tmlx: candidate.tmlx, // 投票类型 }); }); }); return candidateArray; } catch (error) { console.error('获取候选人失败:', error); return []; // 返回空数组保持安全 } }; // 新增计算属性 - 获取已选择的候选人 const selectedCandidates = computed(() => { return voters.value .filter(v => v.vote) // 只过滤已投票的 .map(v => ({ id: v.originalid, // 候选人原始ID voteType: v.vote, // 投票类型(A/B/C) voteValue: v.vote === 'A' ? 17 : // 转换类型值 v.vote === 'B' ? 18 : 19, name: v.name, // 候选人姓名 tmlx: v.tmlx // 原始类型 })); }); // 检查是否有选择的候选人 const hasSelectedCandidates = computed(() => { return selectedCandidates.value.length > 0; }); // 投票统计 const votes = reactive({ A: 0, B: 0, C: 0 }); // 计算总票数 const totalVotes = computed(() => { return votes.A + votes.B + votes.C; }); // 投票方法 const canVote = (voter, type) => { // 情况1:用户取消当前选择的类型(总是允许) if (voter.vote === type) return true; // 情况2:用户从其他类型转换到当前类型 if (voter.vote && voter.vote !== type) { if (type === 'A' && votes.A >= 5) return false; if (type === 'B' && votes.B >= 5) return false; if (type === 'C' && votes.C >= 15) return false; } // 情况3:用户首次投票 if (!voter.vote) { if (type === 'A' && votes.A >= 5) return false; if (type === 'B' && votes.B >= 5) return false; if (type === 'C' && votes.C >= 15) return false; if (totalVotes.value >= 25) return false; } return true; }; // 投票方法 const castVote = (voter, type) => { // 如果已投票且点击相同类型,取消投票 if (voter.vote === type) { voter.vote = null; votes[type]--; return; } // 如果之前有投票,先取消 if (voter.vote !== null) { votes[voter.vote]--; } // 投新票 voter.vote = type; votes[type]++; }; //投票人信息 // 添加投票人信息数据模型 const voterInfo = reactive({ name: '', idNumber: '' }); // // 添加基本信息验证 // const isValid = computed(() => { // return voterInfo.name.trim() !== '' && // voterInfo.idNumber.trim() !== '' && // /^\d{17}[\dXx]$/.test(voterInfo.idNumber); // }); // 提交投票 // 防止重复提交 const isSubmitting = ref(false); // 提交投票到API const submitVotes = async () => { console.log("dclx", activeSurvey.value.dclx); // 添加防御性检查 if (!hasSelectedCandidates.value) { showMessage('请先选择候选人', 'error'); return; } // 防止重复提交 if (isSubmitting.value) return; isSubmitting.value = true; try { const mainData = [ { fieldName: "bt", fieldValue: activeSurvey.value.bt }, { fieldName: "tprxm", fieldValue: voterName.value }, { fieldName: "tprsfz", fieldValue: voterIdCard.value }, { fieldName: "mb", fieldValue: activeSurvey.value.id }, { fieldName: "dclx", fieldValue: activeSurvey.value.dclx }, { fieldName: "tffs", fieldValue: activeSurvey.value.tffs } ]; const workflowRequestTableRecords = selectedCandidates.value.map(candidate => ({ workflowRequestTableFields: [ { fieldName: "dcbt", fieldValue: candidate.originalid }, { fieldName: "tmlx", fieldValue: candidate.tmlx }, { fieldName: "dcxx", fieldValue: candidate.vote === 'A' ? 17 : candidate.vote === 'B' ? 18 : 19 } ] })); // console.log('提交数据:', { // mainData, // workflowRequestTableRecords // }); const requestBody = { requestName: activeSurvey.value.bt, // 投票标题 workflowId: 118, // 固定工作流ID mainData, workflowRequestTableRecords }; // 发送POST请求 const response = await fetch('/api/wechat/addInvestigateWorkflow1', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: safeStringify(requestBody)// 使用安全序列化,避免重复引用 }); const result = await response.json(); // 根据 api_status 判断 if (result.api_status === true) { // 成功处理 const successMsg = result.msg || '投票提交成功!'; showMessage('投票提交成功!', 'success'); // 存储已投票标识 localStorage.setItem('voted_' + voterIdCard.value, 'true'); } else { // 处理错误情况 if (result.msg === '你已提交或不满足提交条件') { showMessage(result.msg, 'error'); // 特殊处理:用户已投票,存储标识防止重复提交 localStorage.setItem('voted_' + voterIdCard.value, 'true'); } else { // 其他错误情况 const errorMsg = result.msg ? `提交失败: ${result.msg}` : '未知错误,请稍后重试'; showMessage(errorMsg, 'error'); } } } catch (error) { console.error('网络请求失败:', error); showMessage('网络错误,请检查连接后重试', 'error'); } finally { isSubmitting.value = false; } }; // 重置投票 const resetVotes = () => { if (confirm('确定要重置所有投票吗?')) { voters.value.forEach(voter => { voter.vote = null; }); votes.A = 0; votes.B = 0; votes.C = 0; voterInfo.name = ''; voterInfo.idNumber = ''; } }; //后台响应信息showMessage const showMessage = (message, type = 'error') => { // 更新消息提示状态 alertMessage.value = message; showAlert.value = true; alertType.value = type; // 错误提示停留3秒,成功提示停留2秒 const timeout = type === 'error' ? 3000 : 2000; setTimeout(() => { showAlert.value = false; }, timeout); }; </script> <style scoped> /* 移动端垂直布局 */ @media (max-width: 480px) { .input-group { flex-direction: column; } } /* 平板/桌面端水平布局 */ @media (min-width: 768px) { .input-group { flex-direction: row; } } /* 消息提示样式 */ .alert { padding: 15px; margin-bottom: 20px; border-radius: 4px; text-align: center; position: fixed; top: 20px; left: 50%; transform: translateX(-50%); z-index: 1000; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); min-width: 300px; opacity: 0.95; } .alert.error { background-color: #ffebee; color: #b71c1c; border: 1px solid #ffcdd2; } .alert.success { background-color: #e8f5e9; color: #1b5e20; border: 1px solid #c8e6c9; } .vote-container { max-width: 1200px; margin: 0 auto; padding: 20px; } .stats { display: flex; justify-content: space-between; margin-bottom: 30px; background: #f5f7fa; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .stat { flex: 1; text-align: center; padding: 0 15px; } .progress { height: 20px; background: #e0e0e0; border-radius: 10px; margin: 10px 0; overflow: hidden; } .progress-bar { height: 100%; background: #3498db; transition: width 0.3s; } .voters-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 20px; } .voter-card { background: white; border-radius: 8px; padding: 15px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); transition: transform 0.2s; } .voter-card:hover { transform: translateY(-5px); } .voter-id { color: #777; font-size: 0.9rem; margin-bottom: 15px; } .vote-options { display: flex; justify-content: space-between; } .vote-options button { flex: 1; margin: 0 5px; padding: 8px 0; border: none; border-radius: 4px; cursor: pointer; transition: all 0.2s; } .vote-options button:not(.selected):hover { opacity: 0.9; transform: scale(1.05); } .vote-options button:first-child { background: #ff6b6b; color: white; } .vote-options button:nth-child(2) { background: #4ecdc4; color: white; } .vote-options button:last-child { background: #ffd166; color: white; } .selected { border: 2px solid #2c3e50 !important; font-weight: bold; box-shadow: 0 0 2 rgba(61, 60, 60, 0.5); } .disabled { opacity: 0.5 !important; cursor: not-allowed !important; } .action-buttons { margin-top: 30px; display: flex; justify-content: center; gap: 20px; } .action-buttons button { padding: 12px 30px; border: none; border-radius: 6px; cursor: pointer; font-size: 1rem; font-weight: 600; transition: all 0.2s; } .action-buttons button:first-child { background: #3498db; color: white; } .action-buttons button:first-child:disabled { background: #bdc3c7; cursor: not-allowed; } .action-buttons button:last-child { background: #e74c3c; color: white; } .action-buttons button:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } </style>

filetype

<template>

投票人:{{ voterName }}

身份证:{{ formattedIdCard }}

经理投票

{{ votes.A }} / 5

厂长投票

{{ votes.B }} / 5

副厂长投票

{{ votes.C }} / 15

总票数

{{ totalVotes }} / 25

{{ voter.name }}

ID: {{ voter.id }}

<button @click="castVote(voter, 'A')" :disabled="!canVote(voter, 'A')" :class="{ 'selected': voter.vote === 'A', 'disabled': !canVote(voter, 'A') }" > 经理 </button> <button @click="castVote(voter, 'B')" :disabled="!canVote(voter, 'B')" :class="{ 'selected': voter.vote === 'B', 'disabled': !canVote(voter, 'B') }" > 厂长 </button> <button @click="castVote(voter, 'C')" :disabled="!canVote(voter, 'C')" :class="{ 'selected': voter.vote === 'C', 'disabled': !canVote(voter, 'C') }" > 副厂长 </button>
<button @click="submitVotes" :disabled="!hasSelectedCandidates || isSubmitting">提交投票</button> <button @click="resetVotes" :disabled="isSubmitting">重置投票</button>
</template> <script setup> import { ref, reactive, computed } from 'vue'; import { useRoute } from 'vue-router'; import { onMounted } from 'vue' import { toRaw } from 'vue'; const voters = ref([]); //候选人 const activeSurvey = ref({ id: null, //投票ID bt: '', // 标题 qydcl: '', dclx: '', //投票类型 tffs: '' }); // onMounted生命周期钩子 onMounted(async () => { // 从sessionStorage获取投票人信息并立即清除 const voterInfo = sessionStorage.getItem('voterInfo'); if (voterInfo) { const { name, idCard } = JSON.parse(voterInfo); voterName.value = name; voterIdCard.value = idCard; sessionStorage.removeItem('voterInfo'); } // 加载候选人数据 voters.value = await fetchCandidates(); }); // 获取路由信息 const route = useRoute(); // 添加用于存储投票人信息的变量 const voterName = ref(''); const voterIdCard = ref(''); // 格式化身份证显示(安全脱敏) const formattedIdCard = computed(() => { if (!voterIdCard.value) return ''; // 显示前6位和后4位,中间用*代替 return voterIdCard.value.substring(0, 6) + '******' + voterIdCard.value.substring(voterIdCard.value.length - 4); }); onMounted(() => { // 从sessionStorage获取数据并立即清除 const voterInfo = sessionStorage.getItem('voterInfo'); if (voterInfo) { const { name, idCard } = JSON.parse(voterInfo); voterName.value = name; voterIdCard.value = idCard; // 关键:立即清除存储防止数据残留 sessionStorage.removeItem('voterInfo'); } }); //获取候选人明细 const fetchCandidates = async () => { try { const response = await fetch('/api/wechat/getInvestigate', { method: 'POST', body: JSON.stringify({ id: '9', dcl: '123' }) }); console.log('API响应:', response); const result = await response.json(); if (!result || !result.root) throw new Error('无效API响应'); // 提取候选人数据 const candidateArray = []; let idCounter = 1; // 自增计数器,名称序号 result.root.forEach(rootItem => { if (!rootItem.childEntList) return; rootItem.childEntList.forEach(candidate => { if (!candidate.dcbt || !candidate.dcxbt) return; candidateArray.push({ originalid: candidate.dcbt, name: candidate.dcxbt, vote: null, id:idCounter++, tmlx: candidate.tmlx, // 投票类型 }); }); }); return candidateArray; } catch (error) { console.error('获取候选人失败:', error); return []; // 返回空数组保持安全 } }; // 新增计算属性 - 获取已选择的候选人 const selectedCandidates = computed(() => { return voters.value .filter(v => v.vote) // 只过滤已投票的 .map(v => ({ id: v.originalid, // 候选人原始ID voteType: v.vote, // 投票类型(A/B/C) voteValue: v.vote === 'A' ? 17 : // 转换类型值 v.vote === 'B' ? 18 : 19, name: v.name, // 候选人姓名 tmlx: v.tmlx // 原始类型 })); }); // 检查是否有选择的候选人 const hasSelectedCandidates = computed(() => { return selectedCandidates.value.length > 0; }); // 投票统计 const votes = reactive({ A: 0, B: 0, C: 0 }); // 计算总票数 const totalVotes = computed(() => { return votes.A + votes.B + votes.C; }); // 投票方法 const canVote = (voter, type) => { // 情况1:用户取消当前选择的类型(总是允许) if (voter.vote === type) return true; // 情况2:用户从其他类型转换到当前类型 if (voter.vote && voter.vote !== type) { if (type === 'A' && votes.A >= 5) return false; if (type === 'B' && votes.B >= 5) return false; if (type === 'C' && votes.C >= 15) return false; } // 情况3:用户首次投票 if (!voter.vote) { if (type === 'A' && votes.A >= 5) return false; if (type === 'B' && votes.B >= 5) return false; if (type === 'C' && votes.C >= 15) return false; if (totalVotes.value >= 25) return false; } return true; }; // 投票方法 const castVote = (voter, type) => { // 如果已投票且点击相同类型,取消投票 if (voter.vote === type) { voter.vote = null; votes[type]--; return; } // 如果之前有投票,先取消 if (voter.vote !== null) { votes[voter.vote]--; } // 投新票 voter.vote = type; votes[type]++; }; //投票人信息 // 添加投票人信息数据模型 const voterInfo = reactive({ name: '', idNumber: '' }); // // 添加基本信息验证 // const isValid = computed(() => { // return voterInfo.name.trim() !== '' && // voterInfo.idNumber.trim() !== '' && // /^\d{17}[\dXx]$/.test(voterInfo.idNumber); // }); // 提交投票 // 防止重复提交 const isSubmitting = ref(false); // 提交投票到API const submitVotes = async () => { // 添加防御性检查 if (!hasSelectedCandidates.value) { alert('请先选择候选人'); return; } // 防止重复提交 if (isSubmitting.value) return; isSubmitting.value = true; try { // 按接口规范构建JSON数据结构 const requestData = { requestName: activeSurvey.value.bt, // 调查标题 workflowId: activeSurvey.value.id, // 调查ID mainData: [ { fieldName: "bt", fieldValue: activeSurvey.value.bt }, { fieldName: "tprxm", fieldValue: voterName }, { fieldName: "tprsfz", fieldValue: voterIdCard }, { fieldName: "mb", fieldValue: activeSurvey.value.id }, { fieldName: "dclx", fieldValue: activeSurvey.value.dclx }, { fieldName: "tffs", fieldValue: activeSurvey.value.tffs }, ], workflowRequestTableRecords: selectedCandidates.value.map(candidate => ({ workflowRequestTableFields: [ { fieldName: "dcbt", fieldValue: candidate.originalid }, { fieldName: "tmlx", fieldValue: candidate.tmlx }, { fieldName: "dcxx", fieldValue: candidate.voteValue // 使用转换后的值 } ] })) }; // 深拷贝打破循环引用 const cleanData = JSON.parse(JSON.stringify(requestData)); // 发送POST请求 const response = await fetch('/api/wechat/addInvestigateWorkflow1', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(cleanData) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); alert('投票提交成功!'); // console.log('API响应:', result); // 提交成功后重置表单 // resetVotes(); // 存储已投票标识 localStorage.setItem('voted_' + voterIdCard.value, 'true'); } catch (error) { console.error('提交失败:', error); // 检查是否已投票错误(假设后端返回409状态码) if (error.response?.status === 409) { alert('您已投过票,无法重复提交'); } else { alert('投票提交失败,请重试'); } } finally { isSubmitting.value = false; } }; // 重置投票 const resetVotes = () => { if (confirm('确定要重置所有投票吗?')) { voters.value.forEach(voter => { voter.vote = null; }); votes.A = 0; votes.B = 0; votes.C = 0; voterInfo.name = ''; voterInfo.idNumber = ''; } }; </script> <style scoped> /* 移动端垂直布局 */ @media (max-width: 480px) { .input-group { flex-direction: column; } } /* 平板/桌面端水平布局 */ @media (min-width: 768px) { .input-group { flex-direction: row; } } .vote-container { max-width: 1200px; margin: 0 auto; padding: 20px; } .stats { display: flex; justify-content: space-between; margin-bottom: 30px; background: #f5f7fa; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .stat { flex: 1; text-align: center; padding: 0 15px; } .progress { height: 20px; background: #e0e0e0; border-radius: 10px; margin: 10px 0; overflow: hidden; } .progress-bar { height: 100%; background: #3498db; transition: width 0.3s; } .voters-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 20px; } .voter-card { background: white; border-radius: 8px; padding: 15px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); transition: transform 0.2s; } .voter-card:hover { transform: translateY(-5px); } .voter-id { color: #777; font-size: 0.9rem; margin-bottom: 15px; } .vote-options { display: flex; justify-content: space-between; } .vote-options button { flex: 1; margin: 0 5px; padding: 8px 0; border: none; border-radius: 4px; cursor: pointer; transition: all 0.2s; } .vote-options button:not(.selected):hover { opacity: 0.9; transform: scale(1.05); } .vote-options button:first-child { background: #ff6b6b; color: white; } .vote-options button:nth-child(2) { background: #4ecdc4; color: white; } .vote-options button:last-child { background: #ffd166; color: white; } .selected { border: 2px solid #2c3e50 !important; font-weight: bold; box-shadow: 0 0 2 rgba(61, 60, 60, 0.5); } .disabled { opacity: 0.5 !important; cursor: not-allowed !important; } .action-buttons { margin-top: 30px; display: flex; justify-content: center; gap: 20px; } .action-buttons button { padding: 12px 30px; border: none; border-radius: 6px; cursor: pointer; font-size: 1rem; font-weight: 600; transition: all 0.2s; } .action-buttons button:first-child { background: #3498db; color: white; } .action-buttons button:first-child:disabled { background: #bdc3c7; cursor: not-allowed; } .action-buttons button:last-child { background: #e74c3c; color: white; } .action-buttons button:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } </style> 点击提交后报错:提交失败: TypeError: Converting circular structure to JSON --> starting at object with constructor 'Link' | property 'sub' -> object with constructor 'ReactiveEffect' --- property 'deps' closes the circle at JSON.stringify (<anonymous>) at submitVotes (Vote.vue:317:1) at callWithErrorHandling (runtime-core.esm-bundler.js:199:1) at callWithAsyncErrorHandling (runtime-core.esm-bundler.js:206:1) at HTMLButtonElement.invoker (runtime-dom.esm-bundler.js:729:1) 帮我修改这段代码

filetype

这个是我同事的代码:// 提交数据     show.value = true;     const mainData = [       { "fieldName": "bt", "fieldValue": bt.value },       { "fieldName": "tprxm", "fieldValue": extractedName.value },       { "fieldName": "tprsfz", "fieldValue": userId.value },       { "fieldName": "mb", "fieldValue": id1.value },       { "fieldName": "dclx", "fieldValue": dclx.value },       { "fieldName": "tffs", "fieldValue": tffs.value }     ];         const workflowRequestTableRecords = list.value.map(item => {       const fields = [         { "fieldName": "dcbt", "fieldValue": item.dcbt },         { "fieldName": "tmlx", "fieldValue": item.tmlx }       ];             if (item.tmlx === 0 || item.tmlx === 1) {         fields.push({           "fieldName": "dcxx",           "fieldValue": item.tmlx === 0             ? item.answer             : item.answer.join(',')         });       }             if (item.tmlx === 2) {         fields.push({           "fieldName": "nr",           "fieldValue": item.answer         });       }       return { workflowRequestTableFields: fields };     });axios.post('/api/wechat/addInvestigateWorkflow1', {       requestName: bt.value,       workflowId: 118,       mainData,       workflowRequestTableRecords     })     .then(response => {       show.value = false;       if (response.data.api_status) {         showDialog({ message: '提交成功' }).then(() => {           router.push({ name: 'VoteList' });         });       } else {         showDialog({ message: response.data.msg });       }     })     .catch(() => {       show.value = false;       showDialog({ message: '提交失败,请联系管理员' });     }); 我的代码:<template>

投票人:{{ voterName }}

身份证:{{ formattedIdCard }}

经理投票

{{ votes.A }} / 5

厂长投票

{{ votes.B }} / 5

副厂长投票

{{ votes.C }} / 15

总票数

{{ totalVotes }} / 25

{{ voter.name }}

ID: {{ voter.id }}

<button @click="castVote(voter, 'A')" :disabled="!canVote(voter, 'A')" :class="{ 'selected': voter.vote === 'A', 'disabled': !canVote(voter, 'A') }" > 经理 </button> <button @click="castVote(voter, 'B')" :disabled="!canVote(voter, 'B')" :class="{ 'selected': voter.vote === 'B', 'disabled': !canVote(voter, 'B') }" > 厂长 </button> <button @click="castVote(voter, 'C')" :disabled="!canVote(voter, 'C')" :class="{ 'selected': voter.vote === 'C', 'disabled': !canVote(voter, 'C') }" > 副厂长 </button>
<button @click="submitVotes" :disabled="!hasSelectedCandidates || isSubmitting">提交投票</button> <button @click="resetVotes" :disabled="isSubmitting">重置投票</button>
</template> <script setup> import { ref, reactive, computed } from 'vue'; import { useRoute } from 'vue-router'; import { onMounted } from 'vue' const voters = ref([]); //候选人 const activeSurvey = ref({ id: null, //投票ID bt: '', // 标题 qydcl: '', dclx: '', //投票类型 tffs: '' }); // 添加消息提示状态 const alertMessage = ref(''); const showAlert = ref(false); const alertType = ref(''); // 'success' 或 'error' // 安全序列化函数 function safeStringify(obj) { const seen = new WeakSet(); return JSON.stringify(obj, (key, value) => { if (typeof value === "object" && value !== null) { if (seen.has(value)) { // 检测到循环引用,返回占位符或跳过 return "[Circular Reference Removed]"; } seen.add(value); } return value; }); } // onMounted生命周期钩子 onMounted(async () => { // 从sessionStorage获取投票人信息并立即清除 const voterInfo = sessionStorage.getItem('voterInfo'); if (voterInfo) { const { name, idCard } = JSON.parse(voterInfo); voterName.value = name; voterIdCard.value = idCard; sessionStorage.removeItem('voterInfo'); } // 加载候选人数据 voters.value = await fetchCandidates(); }); // 获取路由信息 const route = useRoute(); // 添加用于存储投票人信息的变量 const voterName = ref(''); const voterIdCard = ref(''); // 格式化身份证显示(安全脱敏) const formattedIdCard = computed(() => { if (!voterIdCard.value) return ''; // 显示前6位和后4位,中间用*代替 return voterIdCard.value.substring(0, 6) + '******' + voterIdCard.value.substring(voterIdCard.value.length - 4); }); onMounted(() => { // 从sessionStorage获取数据并立即清除 const voterInfo = sessionStorage.getItem('voterInfo'); if (voterInfo) { const { name, idCard } = JSON.parse(voterInfo); voterName.value = name; voterIdCard.value = idCard; // 关键:立即清除存储防止数据残留 sessionStorage.removeItem('voterInfo'); } }); //获取候选人明细 const fetchCandidates = async () => { try { const response = await fetch('/api/wechat/getInvestigate', { method: 'POST', body: JSON.stringify({ id: '9', dcl: '123' }) }); console.log('API响应:', response); const result = await response.json(); if (!result || !result.root) throw new Error('无效API响应'); // 提取候选人数据 const candidateArray = []; let idCounter = 1; // 自增计数器,名称序号 result.root.forEach(rootItem => { if (!rootItem.childEntList) return; rootItem.childEntList.forEach(candidate => { if (!candidate.dcbt || !candidate.dcxbt) return; candidateArray.push({ originalid: candidate.dcbt, name: candidate.dcxbt, vote: null, id:idCounter++, tmlx: candidate.tmlx, // 投票类型 }); }); }); return candidateArray; } catch (error) { console.error('获取候选人失败:', error); return []; // 返回空数组保持安全 } }; // 新增计算属性 - 获取已选择的候选人 const selectedCandidates = computed(() => { return voters.value .filter(v => v.vote) // 只过滤已投票的 .map(v => ({ id: v.originalid, // 候选人原始ID voteType: v.vote, // 投票类型(A/B/C) voteValue: v.vote === 'A' ? 17 : // 转换类型值 v.vote === 'B' ? 18 : 19, name: v.name, // 候选人姓名 tmlx: v.tmlx // 原始类型 })); }); // 检查是否有选择的候选人 const hasSelectedCandidates = computed(() => { return selectedCandidates.value.length > 0; }); // 投票统计 const votes = reactive({ A: 0, B: 0, C: 0 }); // 计算总票数 const totalVotes = computed(() => { return votes.A + votes.B + votes.C; }); // 投票方法 const canVote = (voter, type) => { // 情况1:用户取消当前选择的类型(总是允许) if (voter.vote === type) return true; // 情况2:用户从其他类型转换到当前类型 if (voter.vote && voter.vote !== type) { if (type === 'A' && votes.A >= 5) return false; if (type === 'B' && votes.B >= 5) return false; if (type === 'C' && votes.C >= 15) return false; } // 情况3:用户首次投票 if (!voter.vote) { if (type === 'A' && votes.A >= 5) return false; if (type === 'B' && votes.B >= 5) return false; if (type === 'C' && votes.C >= 15) return false; if (totalVotes.value >= 25) return false; } return true; }; // 投票方法 const castVote = (voter, type) => { // 如果已投票且点击相同类型,取消投票 if (voter.vote === type) { voter.vote = null; votes[type]--; return; } // 如果之前有投票,先取消 if (voter.vote !== null) { votes[voter.vote]--; } // 投新票 voter.vote = type; votes[type]++; }; //投票人信息 // 添加投票人信息数据模型 const voterInfo = reactive({ name: '', idNumber: '' }); // // 添加基本信息验证 // const isValid = computed(() => { // return voterInfo.name.trim() !== '' && // voterInfo.idNumber.trim() !== '' && // /^\d{17}[\dXx]$/.test(voterInfo.idNumber); // }); // 提交投票 // 防止重复提交 const isSubmitting = ref(false); // 提交投票到API const submitVotes = async () => { // 添加防御性检查 if (!hasSelectedCandidates.value) { alert('请先选择候选人', 'error'); return; } // 防止重复提交 if (isSubmitting.value) return; isSubmitting.value = true; try { // 按接口规范构建JSON数据结构 const requestData = { mainData: [ { fieldName: "bt", fieldValue: activeSurvey.value.bt }, { fieldName: "tprxm", fieldValue: voterName }, { fieldName: "tprsfz", fieldValue: voterIdCard }, { fieldName: "mb", fieldValue: activeSurvey.value.id }, { fieldName: "dclx", fieldValue: activeSurvey.value.dclx }, { fieldName: "tffs", fieldValue: activeSurvey.value.tffs }, ], workflowRequestTableRecords: selectedCandidates.value.map(candidate => ({ workflowRequestTableFields: [ { fieldName: "dcbt", fieldValue: candidate.originalid }, { fieldName: "tmlx", fieldValue: candidate.tmlx }, { fieldName: "dcxx", fieldValue: candidate.voteValue // 使用转换后的值 } ] })) }; // 安全序列化避免,循环引用 const jsonString = safeStringify(requestData); // 发送POST请求 const response = await fetch('/api/wechat/addInvestigateWorkflow1', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: jsonString // 直接使用序列化后的字符串 }); const result = await response.json(); // 根据API响应显示相应提示 if (result.code === 200) { // 成功处理 if (result.msg) { showMessage(result.msg, 'success'); }else { showMessage('投票提交成功!', 'success'); } // 存储已投票标识 localStorage.setItem('voted_' + voterIdCard.value, 'true'); } else { // 特殊处理"已提交"消息 if (result.msg === '你已提交或不满足提交条件') { showMessage(result.msg, 'error'); } else { // 其他错误处理 showMessage(`提交失败: ${result.msg || '未知错误'}`, 'error'); } } } catch (error) { console.error('提交失败:', error); // 检查是否已投票错误(假设后端返回409状态码) if (error.response?.status === 409) { alert('您已投过票,无法重复提交'); } else { alert('投票提交失败,请重试'); } } finally { isSubmitting.value = false; } }; // 重置投票 const resetVotes = () => { if (confirm('确定要重置所有投票吗?')) { voters.value.forEach(voter => { voter.vote = null; }); votes.A = 0; votes.B = 0; votes.C = 0; voterInfo.name = ''; voterInfo.idNumber = ''; } }; </script> <style scoped> /* 移动端垂直布局 */ @media (max-width: 480px) { .input-group { flex-direction: column; } } /* 平板/桌面端水平布局 */ @media (min-width: 768px) { .input-group { flex-direction: row; } } .vote-container { max-width: 1200px; margin: 0 auto; padding: 20px; } .stats { display: flex; justify-content: space-between; margin-bottom: 30px; background: #f5f7fa; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .stat { flex: 1; text-align: center; padding: 0 15px; } .progress { height: 20px; background: #e0e0e0; border-radius: 10px; margin: 10px 0; overflow: hidden; } .progress-bar { height: 100%; background: #3498db; transition: width 0.3s; } .voters-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 20px; } .voter-card { background: white; border-radius: 8px; padding: 15px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); transition: transform 0.2s; } .voter-card:hover { transform: translateY(-5px); } .voter-id { color: #777; font-size: 0.9rem; margin-bottom: 15px; } .vote-options { display: flex; justify-content: space-between; } .vote-options button { flex: 1; margin: 0 5px; padding: 8px 0; border: none; border-radius: 4px; cursor: pointer; transition: all 0.2s; } .vote-options button:not(.selected):hover { opacity: 0.9; transform: scale(1.05); } .vote-options button:first-child { background: #ff6b6b; color: white; } .vote-options button:nth-child(2) { background: #4ecdc4; color: white; } .vote-options button:last-child { background: #ffd166; color: white; } .selected { border: 2px solid #2c3e50 !important; font-weight: bold; box-shadow: 0 0 2 rgba(61, 60, 60, 0.5); } .disabled { opacity: 0.5 !important; cursor: not-allowed !important; } .action-buttons { margin-top: 30px; display: flex; justify-content: center; gap: 20px; } .action-buttons button { padding: 12px 30px; border: none; border-radius: 6px; cursor: pointer; font-size: 1rem; font-weight: 600; transition: all 0.2s; } .action-buttons button:first-child { background: #3498db; color: white; } .action-buttons button:first-child:disabled { background: #bdc3c7; cursor: not-allowed; } .action-buttons button:last-child { background: #e74c3c; color: white; } .action-buttons button:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } </style> 我提交的数据写到一起了,我想按照我同事的修改

filetype
资源下载链接为: https://pan.quark.cn/s/1bfadf00ae14 “STC单片机电压测量”是一个以STC系列单片机为基础的电压检测应用案例,它涵盖了硬件电路设计、软件编程以及数据处理等核心知识点。STC单片机凭借其低功耗、高性价比和丰富的I/O接口,在电子工程领域得到了广泛应用。 STC是Specialized Technology Corporation的缩写,该公司的单片机基于8051内核,具备内部振荡器、高速运算能力、ISP(在系统编程)和IAP(在应用编程)功能,非常适合用于各种嵌入式控制系统。 在源代码方面,“浅雪”风格的代码通常简洁易懂,非常适合初学者学习。其中,“main.c”文件是程序的入口,包含了电压测量的核心逻辑;“STARTUP.A51”是启动代码,负责初始化单片机的硬件环境;“电压测量_uvopt.bak”和“电压测量_uvproj.bak”可能是Keil编译器的配置文件备份,用于设置编译选项和项目配置。 对于3S锂电池电压测量,3S锂电池由三节锂离子电池串联而成,标称电压为11.1V。测量时需要考虑电池的串联特性,通过分压电路将高电压转换为单片机可接受的范围,并实时监控,防止过充或过放,以确保电池的安全和寿命。 在电压测量电路设计中,“电压测量.lnp”文件可能包含电路布局信息,而“.hex”文件是编译后的机器码,用于烧录到单片机中。电路中通常会使用ADC(模拟数字转换器)将模拟电压信号转换为数字信号供单片机处理。 在软件编程方面,“StringData.h”文件可能包含程序中使用的字符串常量和数据结构定义。处理电压数据时,可能涉及浮点数运算,需要了解STC单片机对浮点数的支持情况,以及如何高效地存储和显示电压值。 用户界面方面,“电压测量.uvgui.kidd”可能是用户界面的配置文件,用于显示测量结果。在嵌入式系统中,用
weixin_38711110
  • 粉丝: 5
上传资源 快速赚钱