LUOHAILIANG\Administrator 2 年之前
當前提交
4d491aedde

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 3 - 0
.vscode/extensions.json

@@ -0,0 +1,3 @@
+{
+  "recommendations": ["Vue.volar"]
+}

+ 14 - 0
index.html

@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+  <head>
+    <meta charset="UTF-8" />
+    <!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
+    <title>ಥ_ಥ</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script src="/config.js"></script>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 30 - 0
package.json

@@ -0,0 +1,30 @@
+{
+  "name": "vant-js-template",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@vueuse/core": "^9.1.0",
+    "axios": "^0.27.2",
+    "dayjs": "^1.11.5",
+    "vant": "4.0.0-beta.0",
+    "vue": "^3.2.37",
+    "vue-router": "^4.1.3"
+  },
+  "devDependencies": {
+    "@iconify/json": "^2.1.92",
+    "@vitejs/plugin-vue": "^3.0.3",
+    "sass": "^1.54.4",
+    "unplugin-auto-import": "^0.11.1",
+    "unplugin-icons": "^0.14.8",
+    "unplugin-vue-components": "^0.22.4",
+    "vite": "^3.0.7",
+    "vite-plugin-windicss": "^1.8.7",
+    "windicss": "^3.5.6"
+  }
+}

文件差異過大導致無法顯示
+ 1303 - 0
pnpm-lock.yaml


+ 38 - 0
public/config.js

@@ -0,0 +1,38 @@
+// 本地开发环境
+const local = {
+  web_pc: 'https://nml.bozedu.net/',
+  web_mobile: 'https://nmlm.bozedu.net/',
+
+  api: 'https://nmlopenapi.bozedu.net/',
+  oss: 'https://nmlopenapi.bozedu.net/'
+}
+
+// 测试服环境
+const development = {
+  web_pc: 'https://nml.bozedu.net/',
+  web_mobile: 'https://nmlm.bozedu.net/',
+
+  api: 'https://nmlopenapi.bozedu.net/',
+  oss: 'https://nmlopenapi.bozedu.net/',
+}
+
+// 正式服环境
+const production = {
+  web_pc: 'https://nml.bozedu.net/',
+  web_mobile: 'https://nmlm.bozedu.net/',
+
+  api: 'https://nmlopenapi.bozedu.net/',
+  oss: 'https://nmlopenapi.bozedu.net/'
+}
+
+function isWhich() {
+  if (window.location.host.includes('nmlm.bozedu.net')) {
+    return development
+  } else if (window.location.host.includes('nmlm.bozedu.net')) {
+    return production
+  } else {
+    return local
+  }
+}
+window.GLOBAL_CONFIG = isWhich()
+

+ 39 - 0
src/App.vue

@@ -0,0 +1,39 @@
+<script setup>
+import { useDark, useToggle, useFullscreen } from '@vueuse/core';
+
+const { toggle } = useFullscreen()
+
+const isDark = useDark()
+const toggleDark = useToggle(isDark)
+
+const theme = computed(() => isDark.value ? 'dark' : 'light')
+const themeVars = {
+
+}
+</script>
+
+<template>
+  <van-config-provider :theme="theme" :theme-vars="themeVars"
+    class="w-screen h-screen bg-light-300 text-dark-900 flex flex-col " :class="isDark && 'bg-dark-800 text-light-900'">
+    <!-- dark/light mode -->
+    <van-nav-bar title="标题">
+      <template #right>
+        <div @click="toggleDark()" class="text-18px">
+          <i-carbon-moon v-show="isDark" class="text-light-900" />
+          <i-carbon-sun v-show="!isDark" class="text-dark-900" />
+        </div>
+      </template>
+    </van-nav-bar>
+
+    <!-- fullscreen -->
+    <!-- <van-nav-bar title="标题" right-text="全屏" @click-right="toggle">
+    </van-nav-bar> -->
+
+    <div class="overflow-y-auto">
+      <router-view></router-view>
+    </div>
+  </van-config-provider>
+</template>
+
+
+

+ 1 - 0
src/assets/vue.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

+ 140 - 0
src/auto-imports.d.ts

@@ -0,0 +1,140 @@
+// Generated by 'unplugin-auto-import'
+export {}
+declare global {
+  const $$: typeof import('vue/macros')['$$']
+  const $: typeof import('vue/macros')['$']
+  const $computed: typeof import('vue/macros')['$computed']
+  const $customRef: typeof import('vue/macros')['$customRef']
+  const $ref: typeof import('vue/macros')['$ref']
+  const $shallowRef: typeof import('vue/macros')['$shallowRef']
+  const $toRef: typeof import('vue/macros')['$toRef']
+  const EffectScope: typeof import('vue')['EffectScope']
+  const REQUEST: typeof import('./utils/request.js')['REQUEST']
+  const computed: typeof import('vue')['computed']
+  const createApp: typeof import('vue')['createApp']
+  const customRef: typeof import('vue')['customRef']
+  const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
+  const defineComponent: typeof import('vue')['defineComponent']
+  const download: typeof import('./utils/request.js')['download']
+  const effectScope: typeof import('vue')['effectScope']
+  const getCurrentInstance: typeof import('vue')['getCurrentInstance']
+  const getCurrentScope: typeof import('vue')['getCurrentScope']
+  const getTimestamp: typeof import('./utils/day.js')['getTimestamp']
+  const h: typeof import('vue')['h']
+  const inject: typeof import('vue')['inject']
+  const isProxy: typeof import('vue')['isProxy']
+  const isReactive: typeof import('vue')['isReactive']
+  const isReadonly: typeof import('vue')['isReadonly']
+  const isRef: typeof import('vue')['isRef']
+  const markRaw: typeof import('vue')['markRaw']
+  const nextTick: typeof import('vue')['nextTick']
+  const onActivated: typeof import('vue')['onActivated']
+  const onBeforeMount: typeof import('vue')['onBeforeMount']
+  const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
+  const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
+  const onDeactivated: typeof import('vue')['onDeactivated']
+  const onErrorCaptured: typeof import('vue')['onErrorCaptured']
+  const onMounted: typeof import('vue')['onMounted']
+  const onRenderTracked: typeof import('vue')['onRenderTracked']
+  const onRenderTriggered: typeof import('vue')['onRenderTriggered']
+  const onScopeDispose: typeof import('vue')['onScopeDispose']
+  const onServerPrefetch: typeof import('vue')['onServerPrefetch']
+  const onUnmounted: typeof import('vue')['onUnmounted']
+  const onUpdated: typeof import('vue')['onUpdated']
+  const paddingLeft: typeof import('./utils/string.js')['paddingLeft']
+  const provide: typeof import('vue')['provide']
+  const reactive: typeof import('vue')['reactive']
+  const readonly: typeof import('vue')['readonly']
+  const ref: typeof import('vue')['ref']
+  const request: typeof import('./utils/request.js')['default']
+  const resolveComponent: typeof import('vue')['resolveComponent']
+  const shallowReactive: typeof import('vue')['shallowReactive']
+  const shallowReadonly: typeof import('vue')['shallowReadonly']
+  const shallowRef: typeof import('vue')['shallowRef']
+  const toRaw: typeof import('vue')['toRaw']
+  const toRef: typeof import('vue')['toRef']
+  const toRefs: typeof import('vue')['toRefs']
+  const triggerRef: typeof import('vue')['triggerRef']
+  const unref: typeof import('vue')['unref']
+  const useAttrs: typeof import('vue')['useAttrs']
+  const useCssModule: typeof import('vue')['useCssModule']
+  const useCssVars: typeof import('vue')['useCssVars']
+  const useRoute: typeof import('vue-router')['useRoute']
+  const useRouter: typeof import('vue-router')['useRouter']
+  const useSlots: typeof import('vue')['useSlots']
+  const watch: typeof import('vue')['watch']
+  const watchEffect: typeof import('vue')['watchEffect']
+  const watchPostEffect: typeof import('vue')['watchPostEffect']
+  const watchSyncEffect: typeof import('vue')['watchSyncEffect']
+}
+// for vue template auto import
+import { UnwrapRef } from 'vue'
+declare module '@vue/runtime-core' {
+  interface ComponentCustomProperties {
+    readonly $$: UnwrapRef<typeof import('vue/macros')['$$']>
+    readonly $: UnwrapRef<typeof import('vue/macros')['$']>
+    readonly $computed: UnwrapRef<typeof import('vue/macros')['$computed']>
+    readonly $customRef: UnwrapRef<typeof import('vue/macros')['$customRef']>
+    readonly $ref: UnwrapRef<typeof import('vue/macros')['$ref']>
+    readonly $shallowRef: UnwrapRef<typeof import('vue/macros')['$shallowRef']>
+    readonly $toRef: UnwrapRef<typeof import('vue/macros')['$toRef']>
+    readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
+    readonly REQUEST: UnwrapRef<typeof import('./utils/request.js')['REQUEST']>
+    readonly computed: UnwrapRef<typeof import('vue')['computed']>
+    readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
+    readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
+    readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
+    readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
+    readonly download: UnwrapRef<typeof import('./utils/request.js')['download']>
+    readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
+    readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
+    readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
+    readonly getTimestamp: UnwrapRef<typeof import('./utils/day.js')['getTimestamp']>
+    readonly h: UnwrapRef<typeof import('vue')['h']>
+    readonly inject: UnwrapRef<typeof import('vue')['inject']>
+    readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
+    readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
+    readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
+    readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
+    readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
+    readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
+    readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
+    readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
+    readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
+    readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
+    readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
+    readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
+    readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
+    readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
+    readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
+    readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
+    readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
+    readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
+    readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
+    readonly paddingLeft: UnwrapRef<typeof import('./utils/string.js')['paddingLeft']>
+    readonly provide: UnwrapRef<typeof import('vue')['provide']>
+    readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
+    readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
+    readonly ref: UnwrapRef<typeof import('vue')['ref']>
+    readonly request: UnwrapRef<typeof import('./utils/request.js')['default']>
+    readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
+    readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
+    readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
+    readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
+    readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
+    readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
+    readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
+    readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
+    readonly unref: UnwrapRef<typeof import('vue')['unref']>
+    readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
+    readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
+    readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
+    readonly useRoute: UnwrapRef<typeof import('vue-router')['useRoute']>
+    readonly useRouter: UnwrapRef<typeof import('vue-router')['useRouter']>
+    readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
+    readonly watch: UnwrapRef<typeof import('vue')['watch']>
+    readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
+    readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
+    readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
+  }
+}

+ 43 - 0
src/components.d.ts

@@ -0,0 +1,43 @@
+// generated by unplugin-vue-components
+// We suggest you to commit this file into source control
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    FileUpload: typeof import('./components/FileUpload/index.vue')['default']
+    ICarbonMoon: typeof import('~icons/carbon/moon')['default']
+    ICarbonSun: typeof import('~icons/carbon/sun')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    VanButton: typeof import('vant/es')['Button']
+    VanCard: typeof import('vant/es')['Card']
+    VanConfigProvider: typeof import('vant/es')['ConfigProvider']
+    VanCountDown: typeof import('vant/es')['CountDown']
+    VanDivider: typeof import('vant/es')['Divider']
+    VanField: typeof import('vant/es')['Field']
+    VanForm: typeof import('vant/es')['Form']
+    VanGrid: typeof import('vant/es')['Grid']
+    VanGridItem: typeof import('vant/es')['GridItem']
+    VanImage: typeof import('vant/es')['Image']
+    VanList: typeof import('vant/es')['List']
+    VanNavBar: typeof import('vant/es')['NavBar']
+    VanOverlay: typeof import('vant/es')['Overlay']
+    VanPopup: typeof import('vant/es')['Popup']
+    VanRate: typeof import('vant/es')['Rate']
+    VanStep: typeof import('vant/es')['Step']
+    VanSteps: typeof import('vant/es')['Steps']
+    VanSticky: typeof import('vant/es')['Sticky']
+    VanSubmitBar: typeof import('vant/es')['SubmitBar']
+    VanSwipe: typeof import('vant/es')['Swipe']
+    VanSwipeItem: typeof import('vant/es')['SwipeItem']
+    VanTab: typeof import('vant/es')['Tab']
+    VanTabbar: typeof import('vant/es')['Tabbar']
+    VanTabbarItem: typeof import('vant/es')['TabbarItem']
+    VanTabs: typeof import('vant/es')['Tabs']
+    VanTag: typeof import('vant/es')['Tag']
+    VanUploader: typeof import('vant/es')['Uploader']
+  }
+}

+ 41 - 0
src/components/FileUpload/index.vue

@@ -0,0 +1,41 @@
+<script setup>
+import { REQUEST } from '~/utils/request';
+
+const props = defineProps({
+  part: String,
+  full: String,
+})
+
+
+const fileList = $ref([])
+
+const handleAfterRead = (fileProxy) => {
+  fileProxy.status = 'uploading'
+  fileProxy.message = '上传中...'
+  const { file } = fileProxy
+  REQUEST['upload']({
+    url: 'upload/main/file',
+    data: { filedata: file }
+  }).then(res => {
+    console.log('res :>> ', res)
+    console.log('fileList :>> ', fileList);
+    if (res.code === '0') {
+      // fileList.value.push(res.data)
+      fileProxy.status = 'done'
+      fileProxy.message = ''
+    } else {
+      fileProxy.status = 'failed'
+      fileProxy.message = '上传失败'
+    }
+  }).catch((err) => {
+    console.error(err)
+  })
+}
+
+
+
+</script>
+
+<template>
+  <van-uploader :after-read="handleAfterRead" v-model="fileList"></van-uploader>
+</template>

+ 19 - 0
src/main.js

@@ -0,0 +1,19 @@
+import { createApp } from 'vue'
+import 'virtual:windi.css';
+
+import App from './App.vue'
+import router from './router/index';
+
+const app = createApp(App)
+app.use(router)
+
+app.mount('#app')
+
+// Toast
+import 'vant/es/toast/style';
+// Dialog
+import 'vant/es/dialog/style';
+// Notify
+import 'vant/es/notify/style';
+// ImagePreview
+import 'vant/es/image-preview/style';

+ 32 - 0
src/pages/_chat/index.vue

@@ -0,0 +1,32 @@
+<script setup>
+const images = [
+  'https://fastly.jsdelivr.net/npm/@vant/assets/apple-1.jpeg',
+  'https://fastly.jsdelivr.net/npm/@vant/assets/apple-2.jpeg',
+];
+</script>
+
+<template>
+  <!-- <div class="flex flex-col justify-center items-center p-4">
+    <van-swipe :autoplay="3000" lazy-render class="rounded-md shadow-md">
+      <van-swipe-item v-for="image in images" :key="image">
+        <img :src="image" />
+      </van-swipe-item>
+    </van-swipe>
+
+    <van-grid :gutter="10" class="mt-4">
+      <van-grid-item v-for="value in 8" :key="value" icon="photo-o" text="文字" />
+    </van-grid>
+  </div>
+
+  <van-tabs sticky swipeable>
+    <van-tab v-for="index in 8" :title="'选项 ' + index">
+      内容 {{ index }}
+    </van-tab>
+  </van-tabs> -->
+
+  <van-tabbar route>
+    <van-tabbar-item icon="home-o" to="/home">首页</van-tabbar-item>
+    <van-tabbar-item icon="chat-o" to="/chat">消息</van-tabbar-item>
+    <van-tabbar-item icon="user-o" to="/user">我的</van-tabbar-item>
+  </van-tabbar>
+</template>

+ 42 - 0
src/pages/_home/index.vue

@@ -0,0 +1,42 @@
+<script setup>
+const images = [
+  'https://fastly.jsdelivr.net/npm/@vant/assets/apple-1.jpeg',
+  'https://fastly.jsdelivr.net/npm/@vant/assets/apple-2.jpeg',
+];
+
+const gridList = [
+  { icon: 'photo-o', text: '求真在线', to: '' },
+  { icon: 'photo-o', text: '线上课堂', to: '' },
+  { icon: 'photo-o', text: '社团选课', to: '/stxk' },
+  { icon: 'photo-o', text: '教师成长', to: '' },
+  { icon: 'photo-o', text: '云尚活动', to: '' },
+  { icon: 'photo-o', text: '校长信箱', to: '' },
+  { icon: 'photo-o', text: '全部应用', to: '' },
+]
+</script>
+
+<template>
+  <div class="flex flex-col justify-center items-center p-4">
+    <van-swipe :autoplay="3000" lazy-render class="rounded-md shadow-md">
+      <van-swipe-item v-for="image in images" :key="image">
+        <img :src="image" />
+      </van-swipe-item>
+    </van-swipe>
+
+    <van-grid :gutter="10" class="mt-4">
+      <van-grid-item v-for="item in gridList" :key="item.text" v-bind="item" />
+    </van-grid>
+  </div>
+
+  <van-tabs sticky swipeable>
+    <van-tab v-for="index in 8" :title="'选项 ' + index">
+      内容 {{ index }}
+    </van-tab>
+  </van-tabs>
+
+  <van-tabbar route>
+    <van-tabbar-item icon="home-o" to="/home">首页</van-tabbar-item>
+    <van-tabbar-item icon="chat-o" to="/chat">消息</van-tabbar-item>
+    <van-tabbar-item icon="user-o" to="/user">我的</van-tabbar-item>
+  </van-tabbar>
+</template>

+ 32 - 0
src/pages/_user/index.vue

@@ -0,0 +1,32 @@
+<script setup>
+const images = [
+  'https://fastly.jsdelivr.net/npm/@vant/assets/apple-1.jpeg',
+  'https://fastly.jsdelivr.net/npm/@vant/assets/apple-2.jpeg',
+];
+</script>
+
+<template>
+  <!-- <div class="flex flex-col justify-center items-center p-4">
+    <van-swipe :autoplay="3000" lazy-render class="rounded-md shadow-md">
+      <van-swipe-item v-for="image in images" :key="image">
+        <img :src="image" />
+      </van-swipe-item>
+    </van-swipe>
+
+    <van-grid :gutter="10" class="mt-4">
+      <van-grid-item v-for="value in 8" :key="value" icon="photo-o" text="文字" />
+    </van-grid>
+  </div>
+
+  <van-tabs sticky swipeable>
+    <van-tab v-for="index in 8" :title="'选项 ' + index">
+      内容 {{ index }}
+    </van-tab>
+  </van-tabs> -->
+
+  <van-tabbar route>
+    <van-tabbar-item icon="home-o" to="/home">首页</van-tabbar-item>
+    <van-tabbar-item icon="chat-o" to="/chat">消息</van-tabbar-item>
+    <van-tabbar-item icon="user-o" to="/user">我的</van-tabbar-item>
+  </van-tabbar>
+</template>

+ 75 - 0
src/pages/stxk/jz/index.vue

@@ -0,0 +1,75 @@
+<script setup>
+import { getTimestamp } from '~/utils/day';
+import { paddingLeft } from '~/utils/string';
+
+function handleNavLeftClick() {
+  router.back()
+}
+
+function handleNavRightClick() {
+  router.push('/stxk/jz/wd')
+}
+
+let list = $ref(Array.from({ length: 8 }).map((_, i) => ({ id: i, title: '15天学习英语基础词汇基础词汇基础词汇', desc: '讲师:张春丽', price: '18.00', thumb: 'https://fastly.jsdelivr.net/npm/@vant/assets/ipad.jpeg', num: 10, enddate: '2022-8-20' })))
+let loading = $ref(false)
+let finished = $ref(false)
+
+function onLoad() {
+  loading = true
+  // finished = false
+  setTimeout(() => {
+    for (let i = 0; i < 4; i++) {
+      list.push({ title: '15天学习英语基础词汇基础词汇基础词汇', desc: '讲师:张春丽', price: '18.00', thumb: 'https://fastly.jsdelivr.net/npm/@vant/assets/ipad.jpeg', num: 10, enddate: '2022-8-20 01:00:00' });
+    }
+
+    // 加载状态结束
+    loading = false;
+
+    // 数据全部加载完成
+    if (list.length >= 20) {
+      finished = true;
+    }
+  }, 1000);
+}
+
+function getCountDownTime(target) {
+  return getTimestamp(target) - getTimestamp()
+}
+
+const router = useRouter()
+function handleClickCard(item) {
+  console.log(item)
+  router.push(`/stxk/jz/kcxq/${item.id}`)
+}
+</script>
+
+<template>
+
+  <van-sticky>
+    <van-nav-bar title="社团选课" right-text="我的社团课程" @click-right="handleNavRightClick" left-arrow
+      @click-left="handleNavLeftClick">
+    </van-nav-bar>
+  </van-sticky>
+
+  <van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" @click.stop>
+    <template v-for="item in list">
+      <van-card :price="item.price" :desc="item.desc" :title="item.title" :thumb="item.thumb"
+        @click="handleClickCard(item)">
+        <template #tag>
+          <van-count-down :time="getCountDownTime(item.enddate)">
+            <template #default="timeData">
+              <span class="bg-opacity-50 text-light-900 bg-dark-900 px-1">
+                {{ paddingLeft(timeData.days * 24 + timeData.hours) }}:{{ paddingLeft(timeData.minutes)
+                }}:{{ paddingLeft(timeData.seconds) }}
+              </span>
+            </template>
+          </van-count-down>
+        </template>
+        <template #num>
+          <span>{{ item.num }}人报名</span>
+        </template>
+      </van-card>
+    </template>
+  </van-list>
+
+</template>

+ 81 - 0
src/pages/stxk/jz/kcxq.vue

@@ -0,0 +1,81 @@
+<script setup>
+const props = defineProps({
+  id: {
+    type: String,
+    default: undefined,
+  },
+})
+
+const router = useRouter()
+function handleNavLeftClick() {
+  router.back()
+}
+
+
+function handleSubmitbarClick(){
+  console.log('handleSubmitbarClick');
+}
+</script>
+
+<template>
+
+  <van-sticky>
+    <van-nav-bar title="课程详情" left-arrow @click-left="handleNavLeftClick">
+    </van-nav-bar>
+  </van-sticky>
+
+  <div class="p-2 w-full">
+    <van-image src="https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg"></van-image>
+
+    <div class="text-sm">
+      <div class="flex text-lg py-1">
+        <div class="mr-4 font-bold">课程标题标题标题</div>
+        <van-tag type="primary">18课时</van-tag>
+      </div>
+      <div class="flex py-1">
+        <div class="mr-4">讲师:张春丽</div>
+        <div>开播倒计时:48小时56分钟</div>
+      </div>
+      <div class="flex py-1">
+        <div>开课时间:</div>
+        <div>2019/12/22 — 2019/12/26(请在开课前报名)</div>
+      </div>
+      <div class="flex py-1">
+        <div>上课地点:</div>
+        <div>6.4km 相城区元和街道366号</div>
+      </div>
+    </div>
+    <van-divider />
+
+    <div class="mb-4">
+      <div class="pre font-bold mb-2">课程简介</div>
+      <div class="text-sm">
+        9:00-20:30(所有乐园时间仅供参考,请于游览当天至有课服务处索取乐园时间表,以获取当天最新的活动时间详情及变更通知。9:00-20:30(所有乐园时间仅供参考,请于游览当天至有课服务处索取乐园时间表,以获取当天最新的活动时间详情及变更通知。
+      </div>
+    </div>
+
+    <div class="mb-10">
+      <div class="pre font-bold mb-2">教师简介</div>
+      <div class="flex text-sm shadow-md p-2">
+        <div class="w-1/3 flex-none flex flex-col items-center">
+          <van-image src="https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg" width="100" height="100" round
+            fit="cover"></van-image>
+          <div>
+            <div>张春丽</div>
+          </div>
+        </div>
+        <ul class="list-disc list-inside">
+          <li>12年教学经验累</li>
+          <li>计在线授课40万人次助攻</li>
+          <li>多个同学获得满分成绩</li>
+          <li>全国优秀教师</li>
+          <li>全国优秀教师</li>
+        </ul>
+      </div>
+    </div>
+
+    <van-submit-bar :price="3050" button-text="立即报名" @submit="handleSubmitbarClick" placeholder  />
+  </div>
+
+
+</template>

+ 39 - 0
src/pages/stxk/jz/wd/index.vue

@@ -0,0 +1,39 @@
+<script setup>
+const handleNavLeftClick = () => {
+  router.back()
+}
+
+let list = $ref(Array.from({ length: 1 }).map((_, i) => ({ id: i, title: '15天学习英语基础词汇基础词汇基础词汇', desc: '讲师:张春丽', price: '18.00', thumb: 'https://fastly.jsdelivr.net/npm/@vant/assets/ipad.jpeg', num: 10, enddate: '2022-8-20' })))
+let loading = $ref(false)
+let finished = $ref(false)
+
+function onLoad() {
+  finished = true
+}
+
+const router = useRouter()
+function handleClickCard(item) {
+  console.log(item)
+  router.push(`/stxk/jz/wd/kcxq/${item.id}`)
+}
+</script>
+
+<template>
+
+  <van-sticky>
+    <van-nav-bar title="我的社团课程" left-arrow @click-left="handleNavLeftClick">
+    </van-nav-bar>
+  </van-sticky>
+
+  <van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
+    <template v-for="item in list">
+      <van-card :price="item.price" :desc="item.desc" :title="item.title" :thumb="item.thumb"
+        @click="handleClickCard(item)">
+        <template #num>
+          <van-tag type="primary">已报名</van-tag>
+        </template>
+      </van-card>
+    </template>
+  </van-list>
+
+</template>

+ 86 - 0
src/pages/stxk/jz/wd/kcapxq.vue

@@ -0,0 +1,86 @@
+<script setup>
+const props = defineProps({
+  id: {
+    type: String,
+    default: undefined,
+  },
+})
+
+const router = useRouter()
+function handleNavLeftClick() {
+  router.back()
+}
+
+const overlayForm = reactive({
+  kcpj: 5,
+  lspj: 5,
+  fwpj: 5,
+  pjnr: '',
+})
+
+function handleOverlaySubmit() {
+  overlayShow = false
+}
+</script>
+
+<template>
+  <van-sticky>
+    <van-nav-bar title="课程安排详情" left-arrow @click-left="handleNavLeftClick">
+    </van-nav-bar>
+  </van-sticky>
+
+  <div class="p-2">
+    <div class="my-2">
+      <div class="font-bold text-lg py-1">第6课时</div>
+      <div class="py-1 flex text-sm text-dark-700 dark:text-light-900">
+        <div class="mr-2">2019/4/21 18:00-20:30</div>
+        <div>张春丽</div>
+      </div>
+      <div class="py-1 flex text-sm text-blue-500 underline">
+        <div>课程教案:</div>
+        <div>教案111111111.doc</div>
+      </div>
+    </div>
+
+    <div>
+      <div class="font-bold text-lg pre my-2">课堂情况反馈</div>
+      <div class="text-sm text-dark-600 dark:text-light-900">
+        9:00-20:30(所有乐园时间仅供参考,请于游览当天至有课服务处索取乐园时间表,以获取当天最新的活动时间详情及变更通知。9:00-20:30(所有乐园时间仅供参考,请于游览当天至有课服务处索取乐园时间表,以获取当天最新的活动时间详情及变更通知。
+        9:00-20:30(所有乐园时间仅供参考,请于游览当天至有课服务处索取乐园时间表,以获取当天最新的活动时间详情及变更通知。9:00-20:30(所有乐园时间仅供参考,请于游览当天至有课服务处索取乐园时间表,以获取当天最新的活动时间详情及变更通知。
+      </div>
+    </div>
+
+
+    <van-divider />
+
+    <div class="text-xs mb-2">课程已结束您可以对此进行评价</div>
+
+    <van-form @submit="handleOverlaySubmit">
+      <van-field name="kcpj" label="课程评价">
+        <template #input>
+          <van-rate v-model="overlayForm.kcpj" />
+        </template>
+      </van-field>
+      <van-field name="lspj" label="老师评价">
+        <template #input>
+          <van-rate v-model="overlayForm.lspj" />
+        </template>
+      </van-field>
+      <van-field name="fwpj" label="服务评价">
+        <template #input>
+          <van-rate v-model="overlayForm.fwpj" />
+        </template>
+      </van-field>
+      <van-field v-model="overlayForm.pjnr" name="pjnr" placeholder="请输入评价内容..." type="textarea" rows="4" maxlength="500" show-word-limit
+        autosize />
+
+      <div class="text-xs text-red-600 mt-2">* 只能对课程评价一次,评价后无法修改,请确认后提交</div>
+
+      <div style="margin: 16px;">
+        <van-button round block type="primary" native-type="submit">
+          提交
+        </van-button>
+      </div>
+    </van-form>
+  </div>
+</template>

+ 171 - 0
src/pages/stxk/jz/wd/kcxq.vue

@@ -0,0 +1,171 @@
+<script setup>
+const props = defineProps({
+  id: {
+    type: String,
+    default: undefined,
+  },
+})
+
+const router = useRouter()
+function handleNavLeftClick() {
+  router.back()
+}
+
+
+function handleDetailBtnClick(item) {
+  console.log('handleDetailBtnClick', item);
+  router.push(`/stxk/jz/wd/kcapxq/${item.id}`)
+  // overlayShow = true
+}
+
+
+// let overlayShow = $ref(false)
+// const overlayForm = reactive({
+//   kcpj: '',
+//   lspj: '',
+//   fwpj: '',
+//   pjnr: '',
+// })
+// function handleOverlaySubmit() {
+//   overlayShow = false
+// }
+</script>
+
+<template>
+
+  <van-sticky>
+    <van-nav-bar title="课程详情" left-arrow @click-left="handleNavLeftClick">
+    </van-nav-bar>
+  </van-sticky>
+
+  <div class="p-2 w-full">
+    <van-image src="https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg"></van-image>
+
+    <div class="text-sm mb-4">
+      <div class="flex text-lg py-1">
+        <div class="mr-4 font-bold">课程标题标题标题</div>
+        <van-tag type="primary">18课时</van-tag>
+      </div>
+      <div class="flex py-1">
+        <div class="mr-4">讲师:张春丽</div>
+      </div>
+      <div class="flex py-1">
+        <div>开课时间:</div>
+        <div>2019/12/22 — 2019/12/26(请在开课前报名)</div>
+      </div>
+      <div class="flex py-1">
+        <div>上课地点:</div>
+        <div>6.4km 相城区元和街道366号</div>
+      </div>
+    </div>
+    <!-- <van-divider /> -->
+
+    <van-tabs shrink swipeable>
+      <van-tab title="课程详情">
+        <div class="my-4">
+          <div class="pre font-bold mb-2">课程简介</div>
+          <div class="text-sm">
+            9:00-20:30(所有乐园时间仅供参考,请于游览当天至有课服务处索取乐园时间表,以获取当天最新的活动时间详情及变更通知。9:00-20:30(所有乐园时间仅供参考,请于游览当天至有课服务处索取乐园时间表,以获取当天最新的活动时间详情及变更通知。
+          </div>
+        </div>
+
+        <div class="mb-10">
+          <div class="pre font-bold mb-2">教师简介</div>
+          <div class="flex text-sm shadow-md p-2">
+            <div class="w-1/3 flex-none flex flex-col items-center">
+              <van-image src="https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg" width="100" height="100" round
+                fit="cover"></van-image>
+              <div>
+                <div>张春丽</div>
+              </div>
+            </div>
+            <ul class="list-disc list-inside">
+              <li>12年教学经验累</li>
+              <li>计在线授课40万人次助攻</li>
+              <li>多个同学获得满分成绩</li>
+              <li>全国优秀教师</li>
+              <li>全国优秀教师</li>
+            </ul>
+          </div>
+        </div>
+      </van-tab>
+      <van-tab title="课程安排">
+        <div class="my-4">
+          <van-steps direction="vertical" :active="-1">
+            <van-step>
+              <div class="text-base">第1节课</div>
+              <div class="flex justify-between items-end">
+                <div>2016-07-10 09:30</div>
+                <van-button plain type="primary" size="small" @click="handleDetailBtnClick(1)">查看详情</van-button>
+              </div>
+            </van-step>
+            <van-step>
+              <div class="text-base">第2节课</div>
+              <div class="flex justify-between items-end">
+                <div>2016-07-10 09:30</div>
+                <van-button plain type="primary" size="small" @click="handleDetailBtnClick(2)">查看详情</van-button>
+              </div>
+            </van-step>
+            <van-step>
+              <div class="text-base">第3节课</div>
+              <div class="flex justify-between items-end">
+                <div>2016-07-10 09:30</div>
+                <van-button plain type="primary" size="small" @click="handleDetailBtnClick(3)">查看详情</van-button>
+              </div>
+            </van-step>
+          </van-steps>
+        </div>
+      </van-tab>
+    </van-tabs>
+
+    <!-- <van-overlay :show="overlayShow" @click="overlayShow = false" class="flex justify-center items-center">
+      <div class="bg-light-900 w-9/10 p-4 rounded-md dark:bg-dark-900" @click.stop>
+        <div class="flex my-2">
+          <div class="font-bold text-lg mr-4">教学安排与课堂情况反馈</div>
+          <van-tag type="primary">第6节课</van-tag>
+        </div>
+
+        <div class="text-sm text-dark-100 dark:text-light-900">
+          9:00-20:30(所有乐园时间仅供参考,请于游览当天至有课服务处索取乐园时间表,以获取当天最新的活动时间详情及变更通知。9:00-20:30(所有乐园时间仅供参考,请于游览当天至有课服务处索取乐园时间表,以获取当天最新的活动时间详情及变更通知。
+          9:00-20:30(所有乐园时间仅供参考,请于游览当天至有课服务处索取乐园时间表,以获取当天最新的活动时间详情及变更通知。9:00-20:30(所有乐园时间仅供参考,请于游览当天至有课服务处索取乐园时间表,以获取当天最新的活动时间详情及变更通知。
+        </div>
+
+        <van-divider />
+
+        <div class="text-sm">课程已结束您可以对此进行评价</div>
+
+        <van-form @submit="handleOverlaySubmit">
+          <van-field name="kcpj" label="课程评价">
+            <template #input>
+              <van-rate v-model="overlayForm.kcpj" />
+            </template>
+          </van-field>
+          <van-field name="lspj" label="老师评价">
+            <template #input>
+              <van-rate v-model="overlayForm.lspj" />
+            </template>
+          </van-field>
+          <van-field name="fwpj" label="服务评价">
+            <template #input>
+              <van-rate v-model="overlayForm.fwpj" />
+            </template>
+          </van-field>
+          <van-field v-model="overlayForm.pjnr" name="pjnr" placeholder="请输入评价内容..." maxlength="500" show-word-limit rows="6"
+            autosize />
+
+          <div class="text-xs text-red-900">* 只能对课程评价一次,评价后无法修改,请确认后提交</div>
+
+          <div style="margin: 16px;">
+            <van-button round block type="primary" native-type="submit">
+              提交
+            </van-button>
+          </div>
+        </van-form>
+      </div>
+    </van-overlay> -->
+
+
+  </div>
+
+
+</template>

+ 63 - 0
src/router/index.js

@@ -0,0 +1,63 @@
+import { createRouter, createWebHashHistory } from "vue-router";
+
+export default createRouter({
+  history: createWebHashHistory(),
+  routes: [
+    {
+      path: '/',
+      redirect: '/home'
+    },
+    {
+      path: '/home',
+      component: () => import("~/pages/_home/index.vue"),
+    },
+    {
+      path: '/chat',
+      component: () => import("~/pages/_chat/index.vue"),
+    },
+    {
+      path: '/user',
+      component: () => import("~/pages/_user/index.vue"),
+    },
+    {
+      path: '/stxk',
+      redirect: '/stxk/jz',
+      children: [
+        {
+          path: 'jz',
+          children: [
+            {
+              path: '',
+              component: () => import("~/pages/stxk/jz/index.vue")
+            },
+            {
+              path: 'kcxq/:id',
+              component: () => import("~/pages/stxk/jz/kcxq.vue"),
+              props: true,
+            },
+            {
+              path: 'wd',
+              children: [
+                {
+                  path: '',
+                  component: () => import("~/pages/stxk/jz/wd/index.vue")
+                },
+                {
+                  path: 'kcxq/:id',
+                  component: () => import("~/pages/stxk/jz/wd/kcxq.vue"),
+                  props: true,
+                },
+                {
+                  path: 'kcapxq/:id',
+                  component: () => import("~/pages/stxk/jz/wd/kcapxq.vue"),
+                  props: true,
+                }
+              ]
+            }
+          ]
+        },
+      ]
+    }
+  ]
+})
+

+ 6 - 0
src/utils/day.js

@@ -0,0 +1,6 @@
+import * as dayjs from 'dayjs';
+
+
+export const getTimestamp = (date) => {
+  return dayjs(date).valueOf();
+}

+ 82 - 0
src/utils/request.js

@@ -0,0 +1,82 @@
+import axios from 'axios'
+import { showFailToast } from 'vant';
+
+const token = ''
+
+const _request = axios.create({
+  baseURL: window.GLOBAL_CONFIG.api,
+  timeout: 3 * 1000,
+  headers: {
+    'Content-Type': 'application/x-www-form-urlencoded',
+  },
+  method: 'post'
+})
+
+_request.interceptors.request.use(
+  async (config) => {
+    if (config.method?.toLocaleLowerCase() === 'get') {
+      config.params = Object.assign({ token, }, config.params)
+    } else {
+      config.data = Object.assign({ token, client: 'web', api: 'json', issubmit: '1', }, config.data)
+    }
+    return config
+  },
+  (error) => {
+    console.error('request error: ', error)
+    return Promise.reject(error)
+  }
+)
+
+// response interceptor
+_request.interceptors.response.use(
+  (response) => {
+    response.data.code = response.data?.code?.toString()
+    response.data.msg = response.data.msg.replaceAll(/<.*?>/g, ' ')
+    const { code, msg } = response.data
+    if (code !== '1') {
+      showFailToast(msg)
+    }
+    return response.data
+  },
+  (error) => {
+    console.error('response error: ' + error)
+    return Promise.reject(error)
+  }
+)
+
+export default _request
+
+const obj2form = (data) => {
+  const formData = new FormData()
+  Object.keys(data).forEach(key => formData.append(key, data[key]))
+  return formData;
+}
+
+export const REQUEST = {
+  empty: axios,
+  default: _request,
+  import: (c) => _request({
+    timeout: 10 * 60 * 1000,
+    transformRequest: [obj2form],
+    ...c
+  }),
+  upload: (c) => _request({
+    timeout: 3 * 60 * 1000,
+    transformRequest: [obj2form],
+    ...c
+  }),
+  download: (c) => _request({
+    timeout: 1 * 60 * 1000,
+    method: 'get',
+    params: { token, limit: 10000, page: 1, api: 'xls', ...c }
+  })
+}
+
+export function download(url, data) {
+  const params = Object.assign({ token, limit: 10000, page: 1, api: 'xls' }, data)
+  const paramsStr = Object.entries(params).map(([k, v]) => `${k}=${v}`).join('&')
+  const el = document.createElement('a')
+  const href = `${window.GLOBAL_CONFIG.api}${url}?${paramsStr}`
+  el.setAttribute('href', href)
+  el.click()
+}

+ 7 - 0
src/utils/string.js

@@ -0,0 +1,7 @@
+export function paddingLeft(str, length = '2', pad = '0') {
+  str = str.toString();
+  if (str.length >= length) {
+    return str;
+  }
+  return paddingLeft(pad + str, length, pad);
+}

+ 75 - 0
vite.config.js

@@ -0,0 +1,75 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import path from 'path'
+import WindiCss from 'vite-plugin-windicss';
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import { VantResolver } from 'unplugin-vue-components/resolvers'
+import Icons from 'unplugin-icons/vite'
+import IconsResolver from 'unplugin-icons/resolver'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  resolve: {
+    alias: {
+      '@': path.resolve(__dirname, 'src'),
+      '~': path.resolve(__dirname, 'src'),
+      // '~components': path.resolve(__dirname, 'src/components'),
+      // '~pages': path.resolve(__dirname, 'src/pages'),
+      // '~utils': path.resolve(__dirname, 'src/utils'),
+      // '~assets': path.resolve(__dirname, 'src/assets'),
+      // '~styles': path.resolve(__dirname, 'src/styles'),
+      // '~lib': path.resolve(__dirname, 'src/lib'),
+      // '~plugins': path.resolve(__dirname, 'src/plugins'),
+      // '~router': path.resolve(__dirname, 'src/router'),
+      // '~store': path.resolve(__dirname, 'src/store'),
+      // '~config': path.resolve(__dirname, 'src/config'),
+      // '~api': path.resolve(__dirname, 'src/api'),
+      // '~constants': path.resolve(__dirname, 'src/constants'),
+      // '~locales': path.resolve(__dirname, 'src/locales'),
+    }
+  },
+  plugins: [
+    vue({
+      reactivityTransform: true
+    }),
+    WindiCss(),
+    AutoImport({
+      dts: 'src/auto-imports.d.ts',
+      imports: ['vue', 'vue/macros', 'vue-router',],
+      dirs: [
+        'src/composables',
+        'src/store',
+        'src/utils',
+      ],
+      resolvers: [VantResolver()],
+      vueTemplate: true,
+    }),
+    Components({
+      dts: 'src/components.d.ts',
+      dirs: ['src/components/'],
+      // allow auto load markdown components under `./src/components/`
+      extensions: ['vue', 'md'],
+      // allow auto import and register components used in markdown
+      include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
+      resolvers: [VantResolver(), IconsResolver()],
+    }),
+    Icons({
+      compiler: 'vue3',
+      autoInstall: true,
+    })
+  ],
+  server: {
+    host: true
+  },
+  build: {
+    rollupOptions: {
+      output: {
+        manualChunks: {
+          axios: ['axios'],
+          vant: ['vant'],
+        }
+      }
+    }
+  }
+})

+ 17 - 0
windi.config.ts

@@ -0,0 +1,17 @@
+import { defineConfig } from 'windicss/helpers'
+
+export default defineConfig({
+  darkMode:'class',
+  shortcuts: {
+    card: 'rounded-xl bg-light-50 p-3 relative box-border',
+    divider: 'w-full h-0 border border-solid border-gray-100 my-2',
+    divider_y: 'w-0 h-full border border-solid border-gray-100 mx-2',
+    icon: 'w-24px h-24px fill-blue-600 bg-light-100 rounded-2px cursor-pointer  mx-4px box-border',
+    icon_reserve:
+      'w-24px h-24px bg-blue-600 fill-light-100 rounded-2px cursor-pointer mx-4px p-2px box-border',
+    flex_center: 'flex justify-center items-center',
+    flex_start: 'flex justify-start items-center',
+    area:'flex bg-gray-100 text-gray-600 my-2 p-4 text-sm h-300px overflow-auto',
+    pre:'before:content-["|"] before:w-4px before:h-full before:inline-block before:text-transparent before:mr-6px before:bg-blue-400'
+  },
+})