Browse Source

删除不必要的文件

Pchen. 5 months ago
parent
commit
d69456efc3

+ 0 - 47
src/api/power.js

@@ -1,47 +0,0 @@
-import request from '../utils/request'
-
-const api = {
-  Account: '/Power/Account',
-  GetChangeRecord: '/Power/GetChangeRecord',
-  GetPowerData: '/Power/GetPowerData'
-}
-
-export function getPowerData(parameter) {
-  return request({
-    url: api.GetPowerData,
-    method: 'post',
-    data: parameter
-  })
-}
-
-export function getChangeRecord(parameter) {
-  return request({
-    url: api.GetChangeRecord,
-    method: 'post',
-    data: parameter
-  })
-}
-
-export function addAccount(parameter) {
-  return request({
-    url: api.Account,
-    method: 'post',
-    data: parameter
-  })
-}
-
-export function deleteAccount(parameter) {
-  return request({
-    url: api.Account,
-    method: 'delete',
-    data: parameter
-  })
-}
-
-export function getAccount(parameter) {
-  return request({
-    url: api.Account,
-    method: 'get',
-    params: parameter
-  })
-}

+ 0 - 22
src/api/qxs.js

@@ -1,22 +0,0 @@
-import request from '../utils/request'
-
-const api = {
-  getBookList: '/QXS/GetBookList',
-  getBookImg: '/QXS/GetBookImg'
-}
-
-export function GetBookList(parameter) {
-  return request({
-    url: api.getBookList,
-    method: 'post',
-    data: parameter
-  })
-}
-
-export function GetBookImg(parameter) {
-  return request({
-    url: api.getBookImg,
-    method: 'get',
-    params: parameter
-  })
-}

+ 1 - 1
src/pages/Login/components/container.vue

@@ -15,7 +15,7 @@ import { useRoute } from 'vue-router'
 
 const route = useRoute()
 
-let current = ref('uniLogin')
+let current = ref('login')
 
 const changeMode = (mode) => {
     document.querySelector('.center').className = `center ${mode}`

+ 16 - 3
src/pages/Login/components/login.vue

@@ -28,13 +28,15 @@
                         <icon-check-circle />
                     </template>
                     <template #append>
-                        <img alt="!点我重试" :src="ImageCaptcha" class="captcha" @click="getCaptcha()">
+                        <img alt="!点我重试" :src="ImageCaptcha" class="captcha" @click="getCaptcha()" v-if="!captchaLoading">
+                        <icon-loading  v-else />
                     </template>
                 </a-input>
             </a-form-item>
 
             <div class="forgetpass">
-                <a-button type="text" @click="emit('changeMode', 'uniLogin')">快捷登录</a-button>
+                <!-- <a-button type="text" @click="emit('changeMode', 'uniLogin')">快捷登录</a-button> -->
+                <div class="tip">由于业务变更 现无法通过QQ/微信快捷登录<br>快捷登录用户可在注册新账号后提交工单联系客服找回原账号</div>
                 <a-button type="text" @click="emit('changeMode', 'register')">注册账号</a-button>
             </div>
 
@@ -61,6 +63,7 @@ const from = route.query.from
 
 const userStore = useUserStore()
 
+let captchaLoading = ref(false)
 let CaptchaId = ref('')
 let ImageCaptcha = ref('')
 let loading = ref(false)
@@ -72,6 +75,7 @@ let form = reactive({
 
 const getCaptcha = async () => {
     try {
+        captchaLoading.value = true
         const res = await getImageCaptcha()
         if (!res || res.code != 0)
             return requestFailed('获取图片验证码失败!' + res?.msg ?? '')
@@ -79,6 +83,8 @@ const getCaptcha = async () => {
         CaptchaId.value = res.data.id
     } catch (error) {
         requestFailed('获取图片验证码失败!')
+    } finally {
+        captchaLoading.value = false
     }
 }
 
@@ -152,6 +158,14 @@ const requestFailed = (msg) => {
     font-family: Alimama ShuHeiTi, -apple-system, BlinkMacSystemFont;
 }
 
+.tip {
+    color: #777;
+    font-size: 0.9em;
+    text-align: left;
+    margin-bottom: 20px;
+    margin-left: -8px;
+}
+
 .forgetpass {
     width: 100%;
     margin-top: -10px;
@@ -162,6 +176,5 @@ const requestFailed = (msg) => {
 .captcha {
     max-height: 35px;
     margin-right: -10px;
-
 }
 </style>

+ 15 - 7
src/pages/Login/components/register.vue

@@ -36,15 +36,14 @@
                 </a-input-password>
             </a-form-item>
 
-            <a-form-item field="email" hide-label :rules="[{ type: 'email', required: true, message: '请填写正确的邮箱地址' }]"
+            <!-- <a-form-item field="email" hide-label :rules="[{ type: 'email', required: true, message: '请填写正确的邮箱地址' }]"
                 :validate-trigger="['change']">
                 <a-input placeholder="请输入邮箱" allow-clear v-model="form.email">
                     <template #prefix>
                         <icon-email />
                     </template>
                 </a-input>
-            </a-form-item>
-
+            </a-form-item> -->
 
             <a-form-item field="captcha" hide-label :rules="[{ length: 4, required: true, message: '请正确填写图片验证码' }]">
                 <a-input placeholder="请输入图片验证码" allow-clear v-model="form.captcha">
@@ -52,12 +51,14 @@
                         <icon-check-circle />
                     </template>
                     <template #append>
-                        <img alt="!点我重试" :src="ImageCaptcha" class="captcha" @click="getCaptcha()">
+                        <img alt="!点我重试" :src="ImageCaptcha" class="captcha" @click="getCaptcha()"
+                            v-if="!captchaLoading">
+                        <icon-loading v-else />
                     </template>
                 </a-input>
             </a-form-item>
 
-            <a-form-item field="code" hide-label :rules="[{ length: 6, required: true, message: '请正确填写邮箱验证码' }]">
+            <!-- <a-form-item field="code" hide-label :rules="[{ length: 6, required: true, message: '请正确填写邮箱验证码' }]">
                 <a-input placeholder="请输入邮箱验证码" allow-clear v-model="form.code">
                     <template #prefix>
                         <icon-code-square />
@@ -69,7 +70,7 @@
                         </a-button>
                     </template>
                 </a-input>
-            </a-form-item>
+            </a-form-item> -->
 
             <a-button type="text" class="forgetpass" @click="emit('changeMode', 'login')">已有账号,去登录</a-button>
 
@@ -88,6 +89,8 @@ import { ref, reactive, defineEmits } from 'vue'
 const emit = defineEmits(['changeMode'])
 
 const formRef = ref(null)
+
+let captchaLoading = ref(false)
 let CaptchaId = ref('')
 let ImageCaptcha = ref('')
 let form = reactive({
@@ -151,6 +154,7 @@ const SendEmail = async () => {
 
 const getCaptcha = async () => {
     try {
+        captchaLoading.value = true
         const res = await getImageCaptcha()
         if (!res || res.code != 0)
             return requestFailed('获取图片验证码失败!' + res?.msg ?? '')
@@ -158,6 +162,8 @@ const getCaptcha = async () => {
         CaptchaId.value = res.data.id
     } catch (error) {
         requestFailed('获取图片验证码失败!')
+    } finally {
+        captchaLoading.value = false
     }
 }
 
@@ -172,8 +178,10 @@ const handleSubmit = async ({ values, errors }) => {
         if (data.password !== data.password2) return Message.error('请确保两次输入的密码一致!')
 
         data.password = btoa(data.password)
+        data.id = CaptchaId.value
+
         const res = await register(data)
-        if(!res || res.code !== 0)
+        if (!res || res.code !== 0)
             return requestFailed(res.msg)
         Message.success('注册成功!请登录')
         emit('changeMode', 'login')

+ 0 - 1
src/pages/admin/goods/goodsList.vue

@@ -164,7 +164,6 @@ const getList = async () => {
             })
 
         data.value = res.data
-        console.log(res.data)
         pagination.total = res.pagination.total
     } catch (error) {
         Notification.error({

+ 1 - 1
src/pages/admin/lepaoAccount/accountList.vue

@@ -245,7 +245,7 @@ const data = ref([])
 const state = [
     { label: '全部', value: -1 }, { label: '需登录', value: 0 }, { label: '正常', value: 1 }, { label: '状态异常', value: 2 }
 ]
-const area = ["兰花湖校区跑区", "主校区北跑区", "主校区南跑区", "重庆工商大学茶园校区"]
+const area = ["重庆工程学院南泉校区", "重庆工程学院双桥校区"]
 
 const columns = [
     {

+ 0 - 27
src/pages/face/components/doc.vue

@@ -1,27 +0,0 @@
-<template>
-    <div style="text-align: left;">
-        <p style="text-indent: 2em;">为保障识别准确性与用户隐私,请在使用人脸采集功能前阅读并遵守下列须知。</p>
-
-        <h3>一、拍摄与画面要求</h3>
-        <ol>
-            <li>请使用设备原生相机拍摄,确保画面清晰并<strong>关闭美颜/磨皮/滤镜</strong>等图像后处理功能。</li>
-            <li>拍摄时面部不得被遮挡:不得佩戴帽子、口罩;刘海不得覆盖眼睛;请避免使用手势或其他物品遮挡面部。</li>
-            <li>画面应完整包含面部轮廓:从<strong>头顶发尖</strong>至<strong>肩部锁骨</strong>位置均应在取景范围内。</li>
-        </ol>
-
-        <h3>二、光线与环境</h3>
-        <ol>
-            <li>避免强光直射或逆光拍摄,以免影响识别效果。</li>
-            <li>如佩戴眼镜,请调整角度或摘除以减少镜片反光。</li>
-            <li>推荐在光线均匀、背景干净的环境下拍摄。</li>
-        </ol>
-
-        <h3>三、数据使用范围与隐私承诺</h3>
-        <ol>
-            <li>RunForge 仅在<strong>校园乐跑的人脸识别</strong>环节使用所采集的人脸数据,不作其他用途。</li>
-            <li>所有人脸数据将被妥善保存与加密保护;未经数据主体的明确同意,RunForge 不会向第三方披露或挪用该类数据。</li>
-            <li>若需删除或更改您的数据,请联系RunForge客服。</li>
-        </ol>
-    </div>
-</template>
-

+ 0 - 527
src/pages/face/components/faceReco.vue

@@ -1,527 +0,0 @@
-<template>
-  <div class="container">
-    <div v-if="step === 1">
-      <div class="faceWindowWrapper">
-        <div class="faceWindow" :style="{ borderColor: tagColor }">
-          <video id="page_draw-video" muted playsinline></video>
-        </div>
-        <div class="changeButton">
-          <a-button type="primary" shape="circle"
-            @click="state.constraints.video.facingMode === 'user' ? state.constraints.video.facingMode = 'environment' : state.constraints.video.facingMode = 'user'">
-            <icon-sync />
-          </a-button>
-        </div>
-      </div>
-
-      <div class="faceInfo">
-        <a-tag size="large" :color="tagColor">
-          {{ tagInfo }}
-        </a-tag>
-      </div>
-      <div class="countTime" v-if="state.isRecording">
-        <span>还需等待:</span>
-        <span class="time">{{ recTime }}</span>
-        <span>s</span>
-      </div>
-    </div>
-
-    <div v-if="step === 2" class="spin">
-      <a-spin dot tip="信息上传中,请勿离开此界面..." />
-    </div>
-
-    <div v-if="step === 3">
-      <a-result status="success" title="采集完成">
-        <template #extra>
-          <a-space>
-            <a-button type='primary' @click="retry()">返回</a-button>
-          </a-space>
-        </template>
-      </a-result>
-    </div>
-
-    <div v-if="step === 4">
-      <a-result status="error" title="采集失败">
-        <template #extra>
-          <a-space>
-            <a-button type='primary' @click="retry()">返回重试</a-button>
-          </a-space>
-        </template>
-      </a-result>
-    </div>
-
-    <div class="userInfo">
-      <div class="left">
-        <a-avatar>
-          <img :alt="props.userInfo.name ?? props.userInfo.student_num"
-            :src="props.userInfo.user_avatar ?? 'https://lepao-cloud.xxoo365.top/view.php/25aa126dc406974ff3579a99a2c6501a.png'" />
-        </a-avatar>
-      </div>
-      <div class="right">
-        <div class="name">{{ props.userInfo.name }}</div>
-        <div class="sub">{{ props.userInfo.academy_name }}</div>
-      </div>
-    </div>
-  </div>
-
-  <div style="display: none">
-    <div v-show="!state.netsLoadModel">
-      <img id="page_draw-img-target" :src="props.userInfo.face_img" />
-      <canvas id="page_draw-canvas-target"></canvas>
-      <canvas id="page_draw-video-canvas"></canvas>
-    </div>
-  </div>
-
-  <div class="loading" v-if="state.netsLoadModel">
-    <div class="loadingTip">
-      <icon-loading :size="32" />
-      <div class="text">努力加载中</div>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { Message, Modal } from "@arco-design/web-vue"
-import * as faceapi from "@vladmandic/face-api"
-import axios from 'axios'
-import storage from 'store'
-import { ref, onMounted, onUnmounted, reactive, watch } from "vue"
-
-const tagInfo = ref('请将面部完全放置在取景框中央')
-const tagColor = ref('blue')
-const step = ref(1)
-const recTime = ref(10)
-let isSuccessRecording = false
-let countdownTimer = null
-
-const props = defineProps({
-  userInfo: {
-    type: Object,
-    required: true
-  }
-})
-
-/**属性状态 */
-const state = reactive({
-  netsLoadModel: true,
-  netsType: "tinyFaceDetector",
-  netsOptions: {
-    ssdMobilenetv1: undefined,
-    tinyFaceDetector: undefined,
-  },
-  faceMatcher: null,
-  targetImgEl: null,
-  targetCanvasEl: null,
-  discernVideoEl: null,
-  discernCanvasEl: null,
-  timer: 0,
-  constraints: {
-    audio: false,
-    video: {
-      width: { min: 320, ideal: 720, max: 1280 },
-      height: { min: 200, ideal: 480, max: 720 },
-      frameRate: { min: 7, ideal: 15, max: 30 },
-      facingMode: "user",
-    },
-  },
-  stream: null,
-
-  /** 录制相关 */
-  recorder: null,
-  recordingChunks: [],
-  recordingTimer: null,
-  isRecording: false,
-  lastBlob: null,
-  failCount: 0,
-  uploading: false, // 上传状态
-})
-
-/**启动录制 */
-function startRecording() {
-  if (state.isRecording || !state.stream) return
-  state.recordingChunks = []
-  state.recorder = new MediaRecorder(state.stream, { mimeType: "video/webm" })
-
-  state.recorder.ondataavailable = (e) => {
-    if (e.data.size > 0) {
-      state.recordingChunks.push(e.data)
-    }
-  }
-
-  // 停止录制时
-  state.recorder.onstop = () => {
-    const blob = new Blob(state.recordingChunks, { type: "video/webm" })
-
-    if (isSuccessRecording && blob) {
-      uploadVideo(blob)
-    }
-  }
-
-  state.recorder.start()
-  state.isRecording = true
-  recTime.value = 10   // ✅ 开始时重置倒计时
-
-  // 倒计时逻辑
-  countdownTimer = setInterval(() => {
-    if (recTime.value > 0) {
-      recTime.value -= 1
-    }
-  }, 1000)
-
-  // 10秒后自动停止并标记为成功
-  state.recordingTimer = setTimeout(() => {
-    isSuccessRecording = true
-    stopRecording()   // ✅ 成功录制
-    fnClose()
-
-    step.value = 3
-
-  }, recTime.value * 1000)
-}
-
-/**结束录制 */
-function stopRecording() {
-  if (state.recorder && state.isRecording) {
-    state.recorder.stop()
-    state.isRecording = false
-
-    clearTimeout(state.recordingTimer)
-    state.recordingTimer = null
-
-    clearInterval(countdownTimer)
-    countdownTimer = null
-    recTime.value = 10
-  }
-}
-
-/**上传视频到服务器 */
-async function uploadVideo(blob) {
-  try {
-    step.value = 2
-    const formData = new FormData()
-    formData.append("student_num", props.userInfo.student_num)
-    formData.append("key", props.userInfo.key)
-    formData.append("upload", blob, "face_record.webm")
-
-    const url = import.meta.env.VITE_APP_API_BASE_URL + '/UploadFaceVideo'
-
-    const res = await axios.post(url, formData, {
-      headers: { "Content-Type": "multipart/form-data" }
-    })
-
-    if (!res || !res.data || res.data.code !== 0) {
-      step.value = 4
-      throw new Error(res?.data?.msg ?? '上传失败,请稍后再试')
-    }
-    Message.success("人脸数据上传成功")
-    step.value = 3
-  } catch (err) {
-    step.value = 4
-    Message.error(err.message ?? "人脸数据上传失败")
-    console.error(err)
-  }
-}
-
-/**重新开始录制 */
-function restartRecording() {
-  stopRecording()
-  // 小延迟确保上一次 recorder 彻底结束
-  setTimeout(() => {
-    startRecording()
-  }, 200)
-}
-
-/**初始化模型加载 */
-async function fnLoadModel() {
-  const modelsPath = `https://lepao-api.xxoo365.top/models`
-  await faceapi.nets.faceLandmark68Net.load(modelsPath)
-  await faceapi.nets.faceRecognitionNet.load(modelsPath)
-  await faceapi.nets.tinyFaceDetector.load(modelsPath)
-  state.netsOptions.tinyFaceDetector = new faceapi.TinyFaceDetectorOptions({
-    inputSize: 416,
-    scoreThreshold: 0.5,
-  })
-
-  state.targetImgEl = document.getElementById("page_draw-img-target")
-  state.discernVideoEl = document.getElementById("page_draw-video")
-  state.discernCanvasEl = document.getElementById("page_draw-video-canvas")
-
-  state.netsLoadModel = false
-}
-
-/**根据模型参数识别绘制--目标图 */
-async function fnRedrawTarget() {
-  const detect = await faceapi
-    .detectAllFaces(state.targetImgEl, state.netsOptions[state.netsType])
-    .withFaceLandmarks()
-    .withFaceDescriptors()
-  if (!detect || detect.length === 0) {
-    state.faceMatcher = null
-    return
-  }
-  state.faceMatcher = new faceapi.FaceMatcher(detect)
-}
-
-/**根据模型参数识别绘制 */
-async function fnRedrawDiscern() {
-  if (!state.faceMatcher) return
-
-  if (state.discernVideoEl.paused) {
-    clearTimeout(state.timer)
-    state.timer = 0
-    return
-  }
-
-  const detect = await faceapi
-    .detectAllFaces(state.discernVideoEl, state.netsOptions[state.netsType])
-    .withFaceLandmarks()
-    .withFaceDescriptors()
-
-  if (!detect || detect.length === 0) {
-    state.failCount++
-    if (state.failCount >= 3) {
-      tagColor.value = 'blue'
-      tagInfo.value = '请将面部完全放置在取景框中央'
-      if (state.isRecording) stopRecording()
-    }
-
-    // 延迟下一次识别,避免直接递归造成栈/CPU 问题
-    clearTimeout(state.timer)
-    state.timer = setTimeout(() => fnRedrawDiscern(), 300)
-    return
-  }
-
-  const dims = faceapi.matchDimensions(
-    state.discernCanvasEl,
-    state.discernVideoEl,
-    true
-  )
-  const result = faceapi.resizeResults(detect, dims)
-  // 若检测到多张脸,逐一判断(若任一匹配成功则开始录制)
-  for (const item of result) {
-    const descriptor = item.descriptor
-    const best = state.faceMatcher.findBestMatch(descriptor)
-
-    if (best) {
-      // 临时加大匹配范围
-      if (best._distance <= 0.9) {
-        state.failCount = 0
-        tagColor.value = 'green'
-        tagInfo.value = '录制中,请确保面部不要离开取景框'
-        if (!state.isRecording) startRecording()
-        // 找到一个合格的人脸就可以停止遍历
-        break
-      } else {
-        state.failCount = 3
-        tagColor.value = 'red'
-        tagInfo.value = `人脸匹配失败,请确保为 ${props?.userInfo?.name} 本人操作`
-        if (state.isRecording) restartRecording()
-      }
-    }
-  }
-
-  // 继续调度下一次识别(0ms 相当于下一事件循环)
-  clearTimeout(state.timer)
-  state.timer = setTimeout(() => fnRedrawDiscern(), 0)
-}
-
-/**启动摄像头视频媒体 */
-async function fnOpen() {
-  if (state.stream !== null) return
-  try {
-    state.stream = {}
-    const stream = await navigator.mediaDevices.getUserMedia(state.constraints)
-    state.stream = stream
-    state.discernVideoEl.srcObject = stream
-    await state.discernVideoEl.play()
-    state.discernCanvasEl.width = state.discernVideoEl.videoWidth
-    state.discernCanvasEl.height = state.discernVideoEl.videoHeight
-    // 稍作延迟再开始识别
-    setTimeout(() => fnRedrawDiscern(), 300)
-  } catch (error) {
-    fnClose()
-    step.value = 4
-    Message.error("视频媒体流获取错误: " + error)
-  }
-}
-
-/**结束摄像头视频媒体 */
-function fnClose() {
-  if (state.stream === null) return
-  try {
-    state.discernVideoEl.pause()
-    state.discernVideoEl.srcObject = null
-    state.stream.getTracks().forEach((track) => track.stop())
-  } catch (err) {
-    console.warn("关闭流时出错:", err)
-  } finally {
-    state.stream = null
-    clearTimeout(state.timer)
-    state.timer = 0
-    stopRecording()
-  }
-}
-
-watch(
-  () => state.constraints.video.facingMode,
-  () => {
-    if (state.stream !== null) {
-      fnClose()
-      fnOpen()
-      const videoEl = document.getElementById('page_draw-video')
-      if (state.constraints.video.facingMode === 'user')
-        videoEl.style.transform = 'scaleX(-1)'
-      else
-        videoEl.style.transform = 'none'
-    } else {
-      fnClose()
-    }
-  }
-)
-
-onMounted(() => {
-  fnLoadModel().then(() => {
-    fnRedrawTarget()
-    fnOpen()
-    isIosSafari()
-  })
-})
-
-onUnmounted(() => {
-  fnClose()
-})
-
-// IOS加载慢
-function isIosSafari() {
-  const ua = window.navigator.userAgent
-
-  // 1. 判断是否是 iOS 设备
-  const isIOS = /iP(hone|od|ad)/.test(ua)
-
-  if (isIOS) {
-    const faceRecoIosShow = storage.get('faceRecoIosShow')
-    if (!faceRecoIosShow)
-      Modal.info({
-        title: 'IOS设备采集提醒',
-        escToClose: false,
-        maskClosable: false,
-        width: '300px',
-        content: `部分IOS设备在首次采集时可能会遇到加载时间长或采集失败等情况。出现此类情况时可刷新页面重新采集。`,
-        onOk: () => {
-          storage.set('faceRecoIosShow', true)
-        }
-      })
-  }
-}
-
-function retry() {
-  window.location.reload()
-}
-</script>
-
-<style lang="less" scoped>
-.container {
-  display: flex;
-  flex-direction: column;
-  justify-content: center;
-  align-items: center;
-}
-
-.faceWindowWrapper {
-  position: relative;
-  width: 270px;
-  height: 270px;
-}
-
-/* 圆形视频框 */
-.faceWindow {
-  width: 100%;
-  height: 100%;
-  border-radius: 50%;
-  border: 5px solid;
-  overflow: hidden;
-  background: #fff;
-  position: relative;
-}
-
-/* 按钮 */
-.changeButton {
-  position: absolute;
-  right: -10px;
-  /* 超出圆形框右边 */
-  bottom: -10px;
-  /* 超出圆形框底边 */
-}
-
-.faceInfo {
-  margin-top: 20px;
-}
-
-.countTime {
-  margin-top: 10px;
-  font-size: 1.1em;
-  font-family: AlibabaSans, -apple-system, BlinkMacSystemFont;
-
-  .time {
-    font-size: 1.3em;
-  }
-}
-
-.faceWindow video {
-  width: 100%;
-  height: 100%;
-  object-fit: cover;
-  /* 保持视频比例并填满圆形 */
-  transform: scaleX(-1);
-}
-
-.spin {
-  display: flex;
-  align-items: center;
-  min-height: 270px;
-}
-
-.userInfo {
-  display: flex;
-  align-items: center;
-  text-align: left;
-  height: 60px;
-  min-width: 300px;
-  margin-top: 20px;
-  border-radius: 10px;
-  background-color: rgba(190, 218, 255, 0.5);
-  padding: 10px;
-
-  .right {
-    margin-left: 10px;
-
-    .name {
-      font-size: 1.2em;
-      font-weight: bold;
-    }
-  }
-}
-
-.loading {
-  position: fixed;
-  top: 0;
-  left: 0;
-  height: 100%;
-  width: 100%;
-  background-color: rgba(0, 0, 0, 0.2);
-  z-index: 9999;
-  border-radius: 10px;
-
-  .loadingTip {
-    position: absolute;
-    top: 50%;
-    left: 50%;
-    transform: translate(-50%, -50%);
-    color: #3370FF;
-
-    .text {
-      font-family: 'Alimama ShuHeiTi';
-      font-size: 1.2em;
-    }
-  }
-}
-</style>

+ 0 - 175
src/pages/face/index.vue

@@ -1,175 +0,0 @@
-<template>
-    <div class="root">
-        <div class="card" v-if="step === 1">
-            <div class="logo">
-                <img alt="RunForge" src="/logo.svg" height="40">
-                <span class="title">RunForge | 人脸采集</span>
-            </div>
-
-            <a-verification-code size="large" style="width: 300px" v-model="face_form.face_code" :formatter="formatCode"
-                @finish="getUserInfo" :error="code_error" :readonly="buttonLoading" />
-            <div class="inputTip">请输入人脸采集码</div>
-
-            <a-button type="primary" shape="round" size="large" @click="getUserInfo"
-                :loading="buttonLoading">开始人脸采集</a-button>
-        </div>
-
-        <div class="card" v-else-if="step === 2">
-            <div class="logo">
-                <img alt="RunForge" src="/logo.svg" height="40">
-                <span class="title">人脸采集注意事项</span>
-            </div>
-            <Doc />
-            <a-button type="primary" shape="round" size="large" @click="step = 3"
-                :loading="buttonLoading">我已阅读并同意</a-button>
-        </div>
-
-        <div class="card" v-else-if="step === 3">
-            <div class="logo">
-                <img alt="RunForge" src="/logo.svg" height="40">
-                <span class="title">RunForge | 人脸采集</span>
-            </div>
-            <FaceReco :userInfo="userInfo" />
-        </div>
-
-        <div class="footer">
-            <span>© {{ new Date().getFullYear() }} RunForge - 智能校园乐跑平台</span>
-        </div>
-    </div>
-</template>
-
-<script setup>
-import { ref, reactive, onMounted } from 'vue'
-import { useRoute } from 'vue-router'
-import { Message } from '@arco-design/web-vue'
-import { BeginFaceReco } from '@/api/lepao'
-import Doc from './components/doc.vue'
-import FaceReco from './components/faceReco.vue'
-
-const route = useRoute()
-
-const step = ref(1)
-const buttonLoading = ref(false)
-const userInfo = ref({})
-
-const code_error = ref(false)
-const face_form = reactive({
-    face_code: ''
-})
-
-function formatCode(inputValue) {
-    code_error.value = false
-
-    const filtered = inputValue.replace(/[^a-zA-Z0-9]/g, '')
-    return filtered.slice(0, 6).toUpperCase()
-}
-
-const getUserInfo = async () => {
-    try {
-        buttonLoading.value = true
-        const face_code = face_form.face_code
-        if (!face_code || face_code.length !== 6)
-            return Message.error('请将信息填写完整')
-
-        const res = await BeginFaceReco({ face_code })
-        if (!res || res.code !== 0) {
-            code_error.value = true
-            return Message.error(res?.msg ?? '获取人脸识别信息失败!请稍后再试')
-        }
-
-        userInfo.value = res.data
-        step.value = 2
-    } catch (error) {
-        return Message.error('获取人脸识别信息失败!请稍后再试')
-    } finally {
-        buttonLoading.value = false
-    }
-}
-
-// 预加载模型
-
-onMounted(() => {
-    if (route.query.code) {
-        face_form.face_code = formatCode(route.query.code)
-        getUserInfo()
-    }
-})
-</script>
-
-<style lang="less" scoped>
-.root {
-    width: 100%;
-    min-height: 100vh;
-    background: linear-gradient(to right, #74ebd5, #ACB6E5);
-    display: flex;
-    flex-direction: column;
-    font-family: 'Arial', sans-serif;
-    text-align: center;
-    align-items: center;
-    justify-content: center;
-
-    .card {
-        background-color: #fff;
-        border-radius: 20px;
-        box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
-        padding: 30px;
-        width: 75%;
-        max-width: 500px;
-        min-width: 300px;
-        min-height: 300px;
-        margin-bottom: 30px;
-        margin-top: 30px;
-        display: flex;
-        flex-direction: column;
-        align-items: center;
-        justify-content: center;
-
-        .logo {
-            display: flex;
-            justify-content: center;
-            align-items: center;
-            height: 60px;
-            margin-bottom: 30px;
-
-            .title {
-                color: #3370FF;
-                font-size: 1.6em;
-                font-weight: bold;
-                margin-left: 10px;
-                font-family: Alimama ShuHeiTi, -apple-system, BlinkMacSystemFont;
-            }
-
-            img {
-                margin-top: 0;
-            }
-        }
-
-        .inputTip {
-            color: #888;
-            font-size: 0.9em;
-            margin-top: 15px;
-            cursor: default;
-        }
-    }
-
-    button {
-        padding: 15px 18px;
-        border: none;
-        border-radius: 10px;
-        background-color: #4A90E2;
-        color: #fff;
-        cursor: pointer;
-        margin-top: 40px;
-    }
-
-    .footer {
-        cursor: pointer;
-        margin-bottom: 15px;
-        color: #777;
-        font-size: 12px;
-        display: flex;
-        align-items: center;
-        justify-content: flex-end;
-    }
-}
-</style>

+ 0 - 12
src/pages/htmlView/index.vue

@@ -1,12 +0,0 @@
-<template>
-  <div style="width: 100%; height: 100%">
-    <iframe :src="url" style="width: 100%; height: 100%; border: none;"></iframe>
-  </div>
-</template>
-
-<script setup>
-import { useRoute } from 'vue-router'
-const route = useRoute()
-const url = route.query.url
-console.log(url)
-</script>

+ 1 - 1
src/pages/lepao/accountList/index.vue

@@ -355,7 +355,7 @@ const handleSearch = (value) => {
     .map(suffix => `${prefix}@${suffix}`)
 }
 
-const area = ["兰花湖校区跑区", "主校区北跑区", "主校区南跑区", "重庆工商大学茶园校区"]
+const area = ["重庆工程学院南泉校区", "重庆工程学院双桥校区"]
 const state = [
   { label: '全部', value: -1 }, { label: '需登录', value: 0 }, { label: '正常', value: 1 }, { label: '状态异常', value: 2 }
 ]

+ 0 - 110
src/pages/openProxy/index.vue

@@ -1,110 +0,0 @@
-<template>
-    <div class="container">
-        <Breadcrumb :items="['乐跑登录', '启动登录器']" />
-        <a-card title="启动乐跑登录器">
-            <a-result title="乐跑登录器未开启" subtitle="点击下方按钮开启登录器" v-if="!ready">
-                <template #extra>
-                    <a-space>
-                        <a-button type="primary" @click="openProxy" :loading="loading">开启登录器</a-button>
-                    </a-space>
-                </template>
-            </a-result>
-            <a-result status="success" title="启动命令已发送" subtitle="请根据下方输出进行操作" v-else>
-                <template #extra>
-                    <a-space>
-                        <a-button type='primary' @click="closeProxy" :loading="loading">关闭登录器</a-button>
-                    </a-space>
-                </template>
-            </a-result>
-            <div class="output" v-if="output.length > 0">
-                <div v-for="(item, index) in output">{{ item }}</div>
-            </div>
-        </a-card>
-    </div>
-</template>
-
-<script setup>
-import { ref, onUnmounted } from 'vue'
-
-const ready = ref(false)
-const loading = ref(false)
-const output = ref([])
-const getData = ref([])
-
-const openProxy = async () => {
-    try {
-        loading.value = true
-        const result = await window.electron.openProxy()
-        loading.value = false
-        ready.value = true
-
-        let msg = `[系统消息] ${result}`
-        if (msg !== output.value[output.value.length - 1])
-            output.value.push(msg)
-
-        window.electron.onProcessOutput(data => {
-            if (data !== getData.value[getData.value.length - 1]) {
-                getData.value.push(data)
-                if (data.includes('>>>')) {
-                    const parts = data.split('>>>');
-
-                    for (let i = 1; i < parts.length; i++) {
-                        const part = parts[i].trim();
-                        if (part && part !== output.value[output.value.length - 1]) {
-                            output.value.push(part)
-                            if(part.includes('学院:'))
-                            output.value.push('[系统消息] 若更新完一个乐跑账号后还需更新,请点击“关闭登录器”按钮后再次打开')
-                        }
-                    }
-                }
-            }
-        })
-
-        window.electron.onProcessClose(data => {
-            let msg = `[系统消息] 乐跑登录器已停止运行`
-            if (output.value[output.value.length - 1] !== msg)
-                output.value.push(msg)
-        })
-
-        window.electron.onProcessError(data => {
-            let msg = `[系统消息] 乐跑登录器启动时出错:${data}`
-            if (output.value[output.value.length - 1] !== msg)
-                output.value.push(msg)
-        })
-    } catch (error) {
-        let msg = `[系统消息] 开启乐跑登录器失败!请确保没有任何程序阻止登录器启动。若有疑问请提交工单联系客服`
-        if (output.value[output.value.length - 1] !== msg)
-            output.value.push(msg)
-    } finally {
-        loading.value = false
-    }
-}
-
-const closeProxy = async () => {
-    loading.value = true
-    await window.electron.killProcess()
-    output.value.push(`[系统消息] 乐跑登录器关闭命令已发送`)
-    ready.value = false
-    loading.value = false
-}
-
-onUnmounted(() => {
-    if(ready.value) {
-        closeProxy()
-    }
-})
-
-</script>
-
-<style scoped>
-.container {
-    padding: 0 20px 20px 20px;
-}
-
-.output {
-    margin: 10px;
-    padding: 10px;
-    min-height: 300px;
-    background-color: rgba(136, 136, 136, 0.2);
-}
-</style>

+ 1 - 1
src/pages/path/pathList.vue

@@ -107,7 +107,7 @@ const data = ref([])
 const state = [
     { label: '全部', value: -1 }, { label: '待审核', value: 0 }, { label: '审核通过', value: 1 }, { label: '审核失败', value: 2 }
 ]
-const areas = ["兰花湖校区跑区", "主校区北跑区", "主校区南跑区", "重庆工商大学茶园校区"]
+const areas = ["重庆工程学院南泉校区", "重庆工程学院双桥校区"]
 
 const columns = [{
     title: 'ID',

+ 0 - 487
src/pages/power/accountList.vue

@@ -1,487 +0,0 @@
-<template>
-  <div class="container">
-    <Breadcrumb :items="['宿舍电费', '定制电费提醒']" />
-
-    <a-card title="定制电费提醒">
-      <a-button type="primary" size="large" @click="editAccount()">
-        <template #icon>
-          <icon-plus />
-        </template>
-        添加提醒任务
-      </a-button>
-
-      <a-alert v-if="notice" style="margin-top: 15px;">{{ notice }}</a-alert>
-
-      <a-table :data="data" :columns="columns" :bordered="false" hoverable class="table" :loading="loading" :scroll="{
-        x: 1600
-      }" :pagination="{ showPageSize: true, showJumper: true, defaultPageSize: 15 }">
-
-        <template #name-filter="{ filterValue, setFilterValue, handleFilterConfirm, handleFilterReset }">
-          <div class="custom-filter">
-            <a-space direction="vertical">
-              <a-input :model-value="filterValue[0]" @input="(value) => setFilterValue([value])" />
-              <div class="custom-filter-footer">
-                <a-button @click="handleFilterReset">重置</a-button>
-                <a-button @click="handleFilterConfirm">确定</a-button>
-              </div>
-            </a-space>
-          </div>
-        </template>
-        <template #balance="{ record }">
-          <a-tag :color="record.balance <= record.lowest ? 'red' : 'green'">¥{{ record.balance
-          }}</a-tag>
-        </template>
-        <template #lowest="{ record }">
-          ¥{{ record.lowest }}
-        </template>
-        <template #update_time="{ record }">
-          {{ stramptoTime(record.update_time) }}
-        </template>
-        <template #optional="{ record }">
-          <div style="display: flex; gap:10px">
-            <a-button @click="GetChangeRecord(record.id)">账单&nbsp;<icon-select-all /></a-button>
-            <a-dropdown :popup-max-height="false" trigger="hover">
-              <a-button>操作&nbsp;<icon-down /></a-button>
-              <template #content>
-                <a-doption @click="editAccount(record)"><icon-edit /> 编辑任务</a-doption>
-                <a-doption @click="DeleteAccount(record)"><icon-delete /> 删除任务</a-doption>
-              </template>
-            </a-dropdown>
-          </div>
-        </template>
-      </a-table>
-    </a-card>
-  </div>
-
-  <!-- 账号编辑对话框 -->
-  <a-modal v-model:visible="visible" title="编辑电费提醒任务" @cancel="handleCancel" @before-ok="handleBeforeOk" draggable
-    :ok-loading="ok_loading" esc-to-close closable>
-    <a-form :model="form">
-      <a-form-item field="area" label="校区" :loading="selectLoading">
-        <a-select v-model="form.area" placeholder="请选择所在校区" default-value="" :options="areas"
-          @change="GetPowerData('dylist', form.area)" />
-      </a-form-item>
-      <a-form-item field="building" label="楼栋" :loading="selectLoading">
-        <a-select v-model="form.building" placeholder="请选择所在楼栋" default-value="" :options="buildings"
-          @change="GetPowerData('mphlist', form.building)" />
-      </a-form-item>
-      <a-form-item field="room" label="宿舍号" :loading="selectLoading">
-        <a-select v-model="form.room" placeholder="请选择所在宿舍" default-value="" :options="rooms" />
-      </a-form-item>
-      <a-form-item field="lowest" label="提醒阈值">
-        <a-input-number v-model="form.lowest" placeholder="请选择提醒阈值" :step="1" :precision="2" />
-      </a-form-item>
-      <a-form-item field="email" label="通知邮箱">
-        <a-auto-complete :data="email" @search="handleSearch" v-model="form.email" placeholder="用于接收电费变更通知"
-          allow-clear />
-      </a-form-item>
-      <a-form-item field="notes" label="备注">
-        <a-textarea v-model="form.notes" placeholder="添加对账号的备注(非必填)" :max-length="{ length: 50 }" allow-clear
-          show-word-limit />
-      </a-form-item>
-    </a-form>
-  </a-modal>
-
-  <!-- 电费变更记录 -->
-  <a-modal v-model:visible="listVisible" title="电费变更记录" esc-to-close closable width="auto" hide-cancel>
-    <a-table :data="changeList" :columns="listColumns" stripe hoverable :loading="listLoading" :pagination="{
-      showPageSize: true,
-      showJumper: true,
-      showTotal: true,
-      pageSize: pagination.pagesize,
-      current: pagination.current,
-      total: pagination.total
-    }" @page-change="handlePageChange" @page-size-change="handlePageSizeChange">
-      <template #balance="{ record }">
-        ¥{{ record.balance }}
-      </template>
-      <template #old_balance="{ record }">
-        ¥{{ record.old_balance }}
-      </template>
-      <template #change="{ record }">
-        <a-tag :color="Number(record.balance) < Number(record.old_balance) ? 'red' : 'green'">¥{{ Number(record.balance
-          - record.old_balance).toFixed(2)
-        }}</a-tag>
-      </template>
-      <template #time="{ record }">
-        {{ stramptoTime(record.time) }}
-      </template>
-    </a-table>
-  </a-modal>
-</template>
-
-<script setup>
-import { ref, reactive, onMounted, h } from 'vue'
-import { getPowerData, addAccount, deleteAccount, getAccount, getChangeRecord } from '@/api/power'
-import { Modal, Notification, Message } from '@arco-design/web-vue'
-import { useRoute } from 'vue-router'
-import { getNotice } from '@/utils/util'
-
-const notice = ref('')
-
-const GetNotice = async () => {
-  const { path } = useRoute()
-  const res = await getNotice(path)
-  notice.value = res
-}
-
-const data = ref([])
-const loading = ref(false)
-
-const listVisible = ref(false)
-const pagination = reactive({
-  total: 0,
-  current: 1,
-  pagesize: 15
-})
-
-// 分页 - 页码变化
-const handlePageChange = (page) => {
-  pagination.current = page
-}
-
-// 分页 - 每页条数变化
-const handlePageSizeChange = (size) => {
-  pagination.pagesize = size
-  pagination.current = 1 // 页大小变化后回到第一页
-}
-
-const changeList = ref([])
-const listLoading = ref(false)
-const GetChangeRecord = async (id) => {
-  try {
-    listLoading.value = true
-    pagination.current = 1
-    listVisible.value = true
-    changeList.value = []
-    const res = await getChangeRecord({ id })
-    if (!res || res.code !== 0)
-      return Notification.error({
-        title: '获取电费变更记录失败!',
-        content: res?.msg ?? '请稍后再试'
-      })
-    changeList.value = res.data
-    pagination.total = res.data.length
-  } catch (error) {
-    Notification.error({
-      title: '获取电费变更记录失败!',
-      content: error.message || '请稍后再试'
-    })
-  } finally {
-    listLoading.value = false
-  }
-}
-
-const visible = ref(false)
-const ok_loading = ref(false)
-const form = reactive({
-  id: null,
-  area: '',
-  building: '',
-  room: '',
-  email: '',
-  notes: '',
-  lowest: 10.00
-})
-
-const columns = [{
-  title: '校区',
-  dataIndex: 'area',
-  width: 120
-}, {
-  title: '楼栋',
-  dataIndex: 'building',
-  width: 130
-}, {
-  title: '寝室号',
-  dataIndex: 'room',
-}, {
-  title: '通知邮箱',
-  dataIndex: 'email',
-  width: 220
-}, {
-  title: '当前余额',
-  slotName: 'balance',
-  width: 120
-}, {
-  title: '触发提醒阈值',
-  slotName: 'lowest',
-  width: 120
-}, {
-  title: '扣费日期',
-  dataIndex: 'koufei_date',
-  width: 170
-}, {
-  title: '上次更新时间',
-  slotName: 'update_time',
-  width: 170
-}, {
-  title: '备注',
-  dataIndex: 'notes'
-}, {
-  title: '操作',
-  slotName: 'optional',
-  width: 200,
-  fixed: 'right'
-}]
-
-const listColumns = [{
-  title: '记录时间',
-  slotName: 'time',
-  width: 170
-}, {
-  title: '扣费时间',
-  dataIndex: 'change_time',
-  width: 170
-}, {
-  title: '电费余额',
-  slotName: 'balance',
-  width: 120
-}, {
-  title: '原余额',
-  slotName: 'old_balance',
-  width: 120
-}, {
-  title: '变动金额',
-  slotName: 'change',
-  width: 120
-}]
-
-const selectLoading = ref(false)
-const areas = ref([])
-const buildings = ref([])
-const rooms = ref([])
-
-const GetPowerData = async (type, pid = '') => {
-  try {
-    const data = {
-      type,
-      pid
-    }
-    const res = await getPowerData(data)
-    if (!res || res.code !== 0)
-      return Notification.error({
-        title: '获取电费信息失败!',
-        content: res?.msg ?? '请稍后再试'
-      })
-    let resdata = res.data.map(item => ({
-      value: item,
-      label: item
-    }))
-    switch (type) {
-      case 'buildlist':
-        areas.value = resdata
-        break
-      case 'dylist':
-        buildings.value = resdata
-        form.building = ''
-        form.room = ''
-        break
-      case 'mphlist':
-        rooms.value = resdata
-        form.room = ''
-        break
-    }
-  } catch (error) {
-    Notification.error({
-      title: '获取电费信息失败!',
-      content: error.message || '请稍后再试'
-    })
-  }
-}
-
-const email = ref([])
-const handleSearch = (value) => {
-  const emailSuffix = ["qq.com", "ctbu.edu.cn", "163.com"]
-  const input = (value || "").trim()
-
-  if (!input) {
-    email.value = []
-    return
-  }
-
-  // 没有输入 @,直接拼接所有后缀
-  if (!input.includes("@")) {
-    email.value = emailSuffix.map(suffix => `${input}@${suffix}`)
-    return
-  }
-
-  // 输入了 @ 但结尾是 @,拼接所有后缀
-  if (input.endsWith("@")) {
-    email.value = emailSuffix.map(suffix => `${input}${suffix}`)
-    return
-  }
-
-  // 输入了 @ 且有部分后缀,智能匹配
-  const [prefix, suffixPart] = input.split("@")
-  email.value = emailSuffix
-    .filter(suffix => suffix.startsWith(suffixPart))
-    .map(suffix => `${prefix}@${suffix}`)
-}
-
-const editAccount = (item) => {
-  if (item) {
-    form.id = item.id
-    form.area = item.area
-    form.building = item.building
-    form.room = item.room
-    form.email = item.email
-    form.notes = item.notes
-    form.lowest = Number(item.lowest) ?? 10.00
-  } else {
-    form.id = null
-    form.area = ''
-    form.building = ''
-    form.room = ''
-    form.email = ''
-    form.notes = ''
-    form.lowest = 10.00
-  }
-  visible.value = true
-}
-
-const handleBeforeOk = async (done) => {
-  try {
-    ok_loading.value = true
-    const { area, building, room, email } = form
-    if (!area || !building || !room || !email) {
-      Message.error('请填写完整的任务信息')
-      return false
-    }
-
-    const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
-    if (!emailRegex.test(email)) {
-      Message.error('请检查邮箱格式是否正确')
-      return false
-    }
-
-    let data = {
-      ...form
-    }
-
-    const res = await addAccount(data)
-    if (!res || res.code !== 0) {
-      Notification.error({
-        title: '保存电费提醒任务失败!',
-        content: res?.msg ?? '请稍后再试'
-      })
-      return false
-    }
-
-    Message.success('保存成功!')
-    done()
-    getAccounts()
-  } catch (error) {
-    Notification.error({
-      title: '保存电费提醒任务失败!',
-      content: error.message || '请稍后再试'
-    })
-    return false
-  } finally {
-    ok_loading.value = false
-  }
-}
-
-const handleCancel = () => {
-  visible.value = false
-}
-
-const getAccounts = async () => {
-  try {
-    loading.value = true
-    const res = await getAccount()
-    if (!res || res.code !== 0)
-      return Notification.error({
-        title: '获取账号列表失败!',
-        content: res?.msg ?? '请稍后再试'
-      })
-    data.value = res.data
-  } catch (error) {
-    Notification.error({
-      title: '获取账号列表失败!',
-      content: error.message || '请稍后再试'
-    })
-  } finally {
-    loading.value = false
-  }
-}
-
-const DeleteAccount = async (item) => {
-  Modal.confirm({
-    title: '删除任务',
-    content: () => h('div', [
-      h('p', '您是否要删除该电费提醒任务?该操作不可逆')
-    ]),
-    onOk: async () => {
-      const res = await deleteAccount({ id: item.id })
-      if (!res || res.code !== 0)
-        return Notification.error({
-          title: '删除失败',
-          content: res?.msg ?? '请稍后再试'
-        })
-      Message.success('删除成功!')
-      getAccounts()
-    }
-  })
-}
-
-const stramptoTime = (time) => {
-  return new Date(time).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })
-}
-
-onMounted(() => {
-  GetPowerData('buildlist')
-  getAccounts()
-  GetNotice()
-})
-
-</script>
-
-<style scoped lang="less">
-.container {
-  padding: 0 20px 20px 20px;
-}
-
-.table {
-  margin-top: 15px;
-
-  .state {
-    display: flex;
-    align-items: center;
-
-    .circle {
-      border-radius: 50%;
-      height: 8px;
-      min-height: 8px;
-      width: 8px;
-      min-width: 8px;
-      margin-right: 5px;
-    }
-
-    .zero {
-      background-color: rgb(var(--orange-6));
-    }
-
-    .one {
-      background-color: rgb(var(--green-6));
-    }
-
-    .else {
-      background-color: rgb(var(--red-6));
-    }
-  }
-}
-
-.custom-filter {
-  padding: 20px;
-  background: var(--color-bg-5);
-  border: 1px solid var(--color-neutral-3);
-  border-radius: var(--border-radius-medium);
-  box-shadow: 0 2px 5px rgb(0 0 0 / 10%);
-}
-
-.custom-filter-footer {
-  display: flex;
-  justify-content: space-between;
-}
-</style>

+ 0 - 242
src/pages/qxs/getBookList.vue

@@ -1,242 +0,0 @@
-<template>
-    <div class="container">
-        <Breadcrumb :items="['趣选书', '书单查询']" />
-
-        <a-card title="趣选书 · 书单查询" class="card">
-            <div class="userLogin">
-                <a-form :model="form" direction="vertical" size="large" :style="{ width: '50%' }">
-                    <a-form-item field="username" label="用户名">
-                        <a-input v-model="form.username" :style="{ width: '320px' }" placeholder="请输入趣选书账户" allow-clear>
-                            <template #prefix>
-                                <icon-user />
-                            </template>
-                        </a-input>
-                        <template #extra>
-                            <div>默认账号:手机号/身份证号/ctbu+学号</div>
-                        </template>
-                    </a-form-item>
-                    <a-form-item field="password" label="密码">
-                        <a-input-password v-model="form.password" placeholder="请输入趣选书密码" :style="{ width: '320px' }"
-                            :defaultVisibility="false" allow-clear>
-                            <template #prefix>
-                                <icon-lock />
-                            </template>
-                        </a-input-password>
-                        <template #extra>
-                            <div>已为您填充默认密码:ctbu123456</div>
-                        </template>
-                    </a-form-item>
-                    <a-form-item>
-                        <a-space>
-                            <a-button @click="getBookList" :loading="loading">查询</a-button>
-                        </a-space>
-                    </a-form-item>
-                </a-form>
-
-                <a-divider direction="vertical" />
-
-                <div class="userInfo">
-                    <a-avatar :style="{ backgroundColor: '#3370ff' }" :size="70">
-                        <IconUser />
-                    </a-avatar>
-
-                    <div class="username">{{ userInfo.userName ?? '待查询' }}</div>
-                    <div class="termName">{{ userInfo.univName ?? '' }} - {{ userInfo.termName ?? '' }}</div>
-                </div>
-            </div>
-
-            <a-alert v-if="notice" style="margin-top: 15px;">{{ notice }}</a-alert>
-
-            <a-watermark :content="['RunForge', 'xxoo365.top']" :alpha="0.5">
-                <a-table :data="data" stripe hoverable :pagination="false" column-resizable class="table"
-                    :loading="loading" :columns="columns">
-                    <template #imgUrl="{ record }">
-                        <a-image width="50" height="50" fit="contain" v-if="record.imgUrl" :src="record.imgUrl" />
-                        <a-avatar shape="square" :size="50" :style="{ backgroundColor: 'rgba(0,0,0,0.25)' }"
-                            v-else-if="!bookInfo[record.isbn]?.img">
-                            <icon-book />
-                        </a-avatar>
-                        <a-image width="50" height="50" fit="contain" v-else :src="bookInfo[record.isbn]?.img" />
-                    </template>
-                    <template #textbookPrice="{ record }">
-                        <span class="oldPrice" v-if="record.textbookPrice"><del>¥{{ record.textbookPrice }}</del></span>
-                        ¥{{
-                            record.textbookDiscountedPrice ?? record.totalDiscountedPrice }}
-                    </template>
-                    <template #crouseName="{ record }">
-                        {{ record.crouseName }}({{ record.courseType }})
-                    </template>
-                    <template #optional="{ record }">
-                        <a-button @click="showDetail(record.isbn)">查看详情</a-button>
-                    </template>
-                </a-table>
-            </a-watermark>
-
-            <div class="total">
-                共找到 {{ data.length }} 本书籍
-            </div>
-        </a-card>
-    </div>
-</template>
-
-<script setup>
-import { ref, reactive, h } from 'vue'
-import { GetBookList, GetBookImg } from '@/api/qxs'
-import { Notification, Modal, Button } from '@arco-design/web-vue'
-import { useRoute } from 'vue-router'
-import { getNotice } from '@/utils/util'
-
-const notice = ref('')
-
-const GetNotice = async () => {
-  const { path } = useRoute()
-  const res = await getNotice(path)
-  notice.value = res
-}
-
-GetNotice()
-
-const form = reactive({
-    username: '',
-    password: 'ctbu123456',
-})
-
-const loading = ref(false)
-const data = ref([])
-const bookInfo = ref({})
-const userInfo = ref({})
-
-const columns = [
-    {
-        title: '封面',
-        slotName: 'imgUrl',
-    }, {
-        title: '书名',
-        dataIndex: 'title',
-    }, {
-        title: '出版社',
-        dataIndex: 'pressName',
-    }, {
-        title: '作者',
-        dataIndex: 'author',
-    }, {
-        title: 'ISBN号',
-        dataIndex: 'isbn',
-    }, {
-        title: '定价',
-        slotName: 'textbookPrice',
-    }, {
-        title: '课程名称',
-        dataIndex: 'courseName',
-    }, {
-        title: '任课教师',
-        dataIndex: 'multiTeacherName',
-    }, {
-        title: '上课班级',
-        dataIndex: 'className',
-    }, {
-        title: '',
-        slotName: 'optional',
-    }
-]
-
-const showDetail = (isbn) => {
-    Modal.info({
-        title: '书籍详情',
-        content: () => h('div', { class: 'info-modal-content' }, [
-            h('div', { style: 'margin-bottom: 10px;' }, `出版日期:${bookInfo.value[isbn].pubdate != '' ? bookInfo.value[isbn].pubdate : '未知'}`),
-            h('div', { style: 'margin-bottom: 10px;' }, `书籍简介:${bookInfo.value[isbn].summary != '' ? bookInfo.value[isbn].summary : '暂无简介'}`)
-        ])
-    })
-}
-
-const getBookInfo = async (isbn) => {
-    try {
-        const res = await GetBookImg({ isbn })
-        if (!res || res.code !== 0)
-            return console.log(`获取书籍信息失败!${res.msg ?? ''}`)
-        bookInfo.value[isbn] = res.data
-    } catch (error) {
-        console.log(`获取书籍信息失败!${error.message}`)
-    }
-}
-
-const getBookList = async () => {
-    try {
-        loading.value = true
-        const { username, password } = form
-        if (!username || !password)
-            return Notification.error({
-                title: '请完善趣选书账号信息!'
-            })
-        const res = await GetBookList({ username, password })
-        if (!res || res.code !== 0)
-            return Notification.error({
-                title: '获取书单失败!',
-                content: res?.msg ?? '请稍后再试'
-            })
-
-        data.value = res.data.bookList || []
-        userInfo.value = res.data.userInfo
-
-        data.value.forEach((item) => {
-            getBookInfo(item.isbn)
-        })
-    } catch (error) {
-        Notification.error({
-            title: '获取书单失败!',
-            content: error.message || '请稍后再试'
-        })
-    } finally {
-        loading.value = false
-    }
-}
-
-</script>
-
-<style lang="less" scoped>
-.container {
-    padding: 0 20px 20px 20px;
-}
-
-.table {
-    margin-top: 15px;
-
-    .oldPrice {
-        color: #777;
-        font-size: 0.8em;
-    }
-}
-
-.card {
-    .userLogin {
-        display: flex;
-        width: 100%;
-        margin: 0 auto;
-
-        .userInfo {
-            display: flex;
-            flex-direction: column;
-            justify-content: center;
-            align-items: center;
-            width: 50%;
-
-            .username {
-                margin-top: 10px;
-                font-size: 1.6em;
-            }
-
-            .termName {
-                margin-top: 10px;
-                font-size: 1.1em;
-            }
-        }
-    }
-
-    .total {
-        text-align: center;
-        margin: 15px;
-        font-size: 0.8em;
-    }
-}
-</style>

+ 1 - 88
src/router/index.js

@@ -15,16 +15,6 @@ const routes = [
             onlyWeb: true
         }
     },
-    {
-        path: "/faceReco",
-        name: "faceReco",
-        component: () => import('../pages/face/index.vue'),
-        meta: {
-            hideInMenu: true,
-            onlyWeb: true,
-            viewport: 'width=device-width, initial-scale=1'
-        }
-    },
     {
         path: "/login",
         name: "login",
@@ -34,45 +24,6 @@ const routes = [
             hideInMenu: true
         }
     },
-    {
-        path: "/proxy",
-        name: "proxy",
-        component: DEFAULT_LAYOUT,
-        meta: {
-            title: '乐跑登录',
-            icon: 'icon-command',
-            onlyElectron: true
-        },
-        children: [
-            {
-                path: 'openProxy',
-                name: 'proxy.openProxy',
-                component: () => import('../pages/openProxy/index.vue'),
-                meta: {
-                    title: '启动登录器'
-                }
-            }
-        ]
-    },
-    {
-        path: "/htmlView",
-        name: "htmlView",
-        component: HTML_VIEW,
-        meta: {
-            title: '第三方页面',
-            hideInMenu: true
-        },
-        children: [
-            {
-                path: 'view',
-                name: 'htmlView.view',
-                component: () => import('../pages/htmlView/index.vue'),
-                meta: {
-                    title: '第三方页面'
-                }
-            }
-        ]
-    },
     {
         path: "/store",
         name: 'store',
@@ -156,44 +107,6 @@ const routes = [
             }
         ]
     },
-    {
-        path: "/qxs",
-        name: "qxs",
-        component: DEFAULT_LAYOUT,
-        meta: {
-            title: '趣选书',
-            icon: 'icon-book'
-        },
-        children: [
-            {
-                path: 'getBookList',
-                name: 'qxs.getBookList',
-                component: () => import('../pages/qxs/getBookList.vue'),
-                meta: {
-                    title: '书单查询'
-                }
-            }
-        ]
-    },
-    {
-        path: "/power",
-        name: "power",
-        component: DEFAULT_LAYOUT,
-        meta: {
-            title: '宿舍电费',
-            icon: 'icon-thunderbolt'
-        },
-        children: [
-            {
-                path: 'accountList',
-                name: 'power.accountList',
-                component: () => import('../pages/power/accountList.vue'),
-                meta: {
-                    title: '定制电费提醒'
-                }
-            }
-        ]
-    },
     {
         path: "/service",
         name: 'service',
@@ -426,7 +339,7 @@ const router = VueRouter.createRouter({
     routes: routes
 })
 
-const allow = ['/', '/login', '/qxs/getBookList', '/htmlView/view', '/download/down', '/faceReco']
+const allow = ['/', '/login', '/htmlView/view', '/download/down']
 
 router.beforeEach(async (to, from, next) => {
     if (!allow.includes(to.path)) {