wangtao пре 1 недеља
родитељ
комит
b755629105

+ 1 - 1
.env

@@ -1,2 +1,2 @@
 # title
-VITE_GLOB_APP_TITLE=Low-Flow
+VITE_GLOB_APP_TITLE=智慧办公系统(U8)

+ 6 - 3
.env.development

@@ -1,5 +1,8 @@
 # 公共基础路径
-VITE_PUBLIC_PATH=/
+VITE_PUBLIC_PATH="/"
 
-# 开发环境接口地址
-VITE_API_URL=/api
+# 测试环境接口地址
+VITE_API_URL="http://192.168.10.165:9089/"
+
+# 图片回显地址
+VITE_IMAGE_URL="http://192.168.10.165:13301"

+ 5 - 2
.env.production

@@ -1,5 +1,8 @@
 # 公共基础路径
-VITE_PUBLIC_PATH=/
+VITE_PUBLIC_PATH="/mobile/#/"
 
 # 线上环境接口地址
-VITE_API_URL="https://demo.lowflow.vip/api"
+VITE_API_URL="https://oa.usky.cn/prod-api/"
+
+# 图片回显地址
+VITE_IMAGE_URL="https://file.usky.cn"

+ 5 - 2
.env.test

@@ -1,5 +1,8 @@
 # 公共基础路径
-VITE_PUBLIC_PATH=/
+VITE_PUBLIC_PATH=""
 
 # 测试环境接口地址
-VITE_API_URL="https://demo.lowflow.vip/api"
+VITE_API_URL="http://192.168.10.165:9089/"
+
+# 图片回显地址
+VITE_IMAGE_URL="http://192.168.10.165:13301"

+ 39 - 0
README.md

@@ -0,0 +1,39 @@
+# low-flow-h5
+
+This template should help get you started developing with Vue 3 in Vite.
+
+## Recommended IDE Setup
+
+[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
+
+## Type Support for `.vue` Imports in TS
+
+TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
+
+## Customize configuration
+
+See [Vite Configuration Reference](https://vitejs.dev/config/).
+
+## Project Setup
+
+```sh
+pnpm install
+```
+
+### Compile and Hot-Reload for Development
+
+```sh
+pnpm dev
+```
+
+### Type-Check, Compile and Minify for Production
+
+```sh
+pnpm build
+```
+
+### Lint with [ESLint](https://eslint.org/)
+
+```sh
+pnpm lint
+```

+ 2 - 2
index.html

@@ -2,9 +2,9 @@
 <html lang="en">
 <head>
   <meta charset="UTF-8">
-  <link rel="icon" href="/favicon.ico">
+  <link rel="icon" href="/favicon.png">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>Vite App</title>
+  <title>智慧办公系统(U8)</title>
   <script>
     ;(function() {
       const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches

BIN
public/favicon.ico


BIN
public/favicon.png


+ 7 - 1
src/api/index.ts

@@ -9,7 +9,7 @@ import axios, { HttpStatusCode } from 'axios'
 import qs from 'qs'
 import type { ResultData } from '@/typings/api'
 import { useAuthStore } from '@/stores/modules/auth'
-import { showFailToast } from 'vant'
+import { showFailToast, showConfirmDialog } from 'vant'
 
 /**
  * axios配置
@@ -72,6 +72,12 @@ class RequestHttp {
             authStore.$reset()
             window.location.reload()
           }
+        }else if (response?.status === HttpStatusCode.Forbidden) {
+          showConfirmDialog({
+            message: '该资源没有访问权限,是否重新登录?'
+          }).then(() => {
+            authStore.logout()
+          })
         } else {
           showFailToast(errMsg || '请求失败')
         }

+ 1 - 1
src/api/modules/file/file.ts

@@ -23,5 +23,5 @@ export const upload = (file: File) => {
 }
 
 export const deleteByIds = (ids: string[]) => {
-  return http.delete('/file/delete', ids)
+  return http.delete('/file', ids)
 }

+ 12 - 0
src/api/modules/oauth/auth.ts

@@ -17,6 +17,10 @@ export interface LoginForm {
   uuid: string
 }
 
+export interface LoginForm2 {
+  salt: string
+}
+
 export interface AuthResponse {
   accessToken: string
   refreshToken: string
@@ -38,6 +42,14 @@ export const login = (loginForm: LoginForm) => {
   return http.post<AuthResponse>('/auth/login', loginForm)
 }
 
+/**
+ * 单点登录
+ * @param LoginForm2
+ */
+export const saltLogin = (LoginForm2: LoginForm2) => {
+  return http.post<AuthResponse>('/auth/saltLogin', LoginForm2)
+}
+
 /**
  * 退出
  */

+ 17 - 1
src/assets/icons/logo.svg

@@ -1 +1,17 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve" fill="#409eff">
+<g>
+	<path d="M29.3,24H2.7c-0.6,0-1.2-0.5-1.2-1.2V4.5c0-0.6,0.5-1.2,1.2-1.2h26.6c0.6,0,1.2,0.5,1.2,1.2v18.3C30.5,23.5,30,24,29.3,24z
+		 M3.1,22.5h25.8V5H3.1V22.5z M23.8,28.6H8.2c-0.6,0-1.2-0.5-1.2-1.2v-1.2C7,25.6,7.5,25,8.2,25h15.6c0.6,0,1.2,0.5,1.2,1.2v1.2
+		C24.9,28.1,24.4,28.6,23.8,28.6z M8.6,27h14.7v-0.4H8.6V27z"/>
+	<g>
+		<path d="M8,15.2V8.7h1.8V15c0,1.4,0.5,2,2.1,2c1.7,0,2.1-0.6,2.1-2V8.7h1.8v6.5c0,2.4-1.4,3.3-4,3.3C9.4,18.5,8,17.7,8,15.2z"/>
+		<path d="M17.6,15.8c0-1.5,0.9-2.1,1.6-2.4v-0.1c-0.8-0.4-1.3-1-1.3-2.2c0-1.6,1-2.6,3.2-2.6s3.2,1,3.2,2.6c0,1.2-0.5,1.8-1.4,2.2
+			v0.1c0.9,0.3,1.7,0.9,1.7,2.5c0,1.8-1.3,2.7-3.5,2.7S17.6,17.7,17.6,15.8z M22.9,15.7c0-1.1-0.6-1.6-1.8-1.6s-1.8,0.5-1.8,1.6
+			c0,0.9,0.5,1.4,1.8,1.4C22.3,17.1,22.9,16.6,22.9,15.7z M22.6,11.3c0-0.9-0.5-1.4-1.6-1.4s-1.6,0.5-1.6,1.4c0,0.8,0.4,1.5,1.6,1.5
+			C22.2,12.7,22.6,12.1,22.6,11.3z"/>
+	</g>
+</g>
+</svg>

BIN
src/assets/images/login_bg.jpg


+ 31 - 8
src/components/Render/components/FileUpload.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import { showToast, type UploaderFileListItem } from 'vant'
-import { upload } from '@/api/modules/file/file'
-
+import { upload, deleteByIds } from '@/api/modules/file/file'
+const fileList = ref<UploaderFileListItem[]>([])
 const props = defineProps<{
   modelValue: string[] | null
   maxSize: number
@@ -12,9 +12,12 @@ const value = computed<UploaderFileListItem[]>({
     return (
       props.modelValue?.map((e) => {
         return {
-          url: e,
+          url: `${import.meta.env.VITE_IMAGE_URL}${e?.url}`,
           isImage: false,
-          status: 'done'
+          id: e?.id,
+          name: e?.name,
+          type: e?.type,
+          uid:e?.uid,
         }
       }) || []
     )
@@ -23,6 +26,7 @@ const value = computed<UploaderFileListItem[]>({
     emits('update:modelValue', val)
   }
 })
+fileList.value  = value.value
 const onOversize = () => {
   showToast(`文件大小不能超过 ${props.maxSize / 1024 / 1024}mb`)
 }
@@ -37,15 +41,30 @@ const beforeRead = (
     all.push(upload(file))
   }
   Promise.all(all).then((res) => {
-    const fileList = res.map((r) => `/api${r.data.url}`)
+    fileList.value = res.map(r => ({
+      url: r.data.url,
+      id: r.data.id,
+      name: r.data.name,
+      type: r.data.type,
+      isImage: false,
+      uid:r.data.id
+    }))
     if (Array.isArray(props.modelValue)) {
-      emits('update:modelValue', [...props.modelValue, ...fileList])
+      emits('update:modelValue', [...props.modelValue, ...fileList.value])
     } else {
-      emits('update:modelValue', fileList)
+      emits('update:modelValue', fileList.value)
     }
   })
   return true
 }
+const afterRead = (file: File) => {
+}
+// 删除文件
+const handleDelete = (file: UploaderFileListItem, sub: UploaderFileListItem[]) => {
+  deleteByIds([fileList.value[sub.index].id])
+  fileList.value.splice(sub.index,1)
+  emits('update:modelValue', fileList.value)
+}
 </script>
 
 <template>
@@ -57,7 +76,11 @@ const beforeRead = (
         v-bind="$attrs"
         @oversize="onOversize"
         :before-read="beforeRead"
-      />
+        :after-read="afterRead"
+        :deletable="true"
+        @delete="handleDelete"
+      >
+    </van-uploader>
     </template>
   </van-field>
 </template>

+ 29 - 7
src/components/Render/components/ImageUpload.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import { showToast, type UploaderFileListItem } from 'vant'
-import { upload } from '@/api/modules/file/file'
-
+import { upload, deleteByIds } from '@/api/modules/file/file'
+const fileList = ref<UploaderFileListItem[]>([])
 const props = defineProps<{
   modelValue: string[] | null
   maxSize: number
@@ -12,9 +12,12 @@ const value = computed<UploaderFileListItem[]>({
     return (
       props.modelValue?.map((e) => {
         return {
-          url: e,
+          url: `${import.meta.env.VITE_IMAGE_URL}${e?.url}`,
           isImage: true,
-          status: 'done'
+          id: e?.id,
+          name: e?.name,
+          type: e?.type,
+          uid: e?.uid,
         }
       }) || []
     )
@@ -23,6 +26,7 @@ const value = computed<UploaderFileListItem[]>({
     emits('update:modelValue', val)
   }
 })
+fileList.value  = value.value
 const onOversize = () => {
   showToast(`文件大小不能超过 ${props.maxSize / 1024 / 1024}mb`)
 }
@@ -37,15 +41,30 @@ const beforeRead = (
     all.push(upload(file))
   }
   Promise.all(all).then((res) => {
-    const fileList = res.map((r) => `/api${r.data.url}`)
+    fileList.value = res.map(r => ({
+      url: r.data.url,
+      id: r.data.id,
+      name: r.data.name,
+      isImage: true,
+      type: r.data.type,
+      uid: r.data.uid,
+    }))
     if (Array.isArray(props.modelValue)) {
-      emits('update:modelValue', [...props.modelValue, ...fileList])
+      emits('update:modelValue', [...props.modelValue, ...fileList.value])
     } else {
-      emits('update:modelValue', fileList)
+      emits('update:modelValue', fileList.value)
     }
   })
   return true
 }
+const afterRead = (file: File) => {
+}
+// 删除文件
+const handleDelete = (file: UploaderFileListItem, sub: UploaderFileListItem[]) => {
+  deleteByIds([fileList.value[sub.index].id])
+  fileList.value.splice(sub.index,1)
+  emits('update:modelValue', fileList.value)
+}
 </script>
 
 <template>
@@ -57,6 +76,9 @@ const beforeRead = (
         v-bind="$attrs"
         @oversize="onOversize"
         :before-read="beforeRead"
+        :after-read="afterRead"
+        :deletable="true"
+        @delete="handleDelete"
       />
     </template>
   </van-field>

+ 12 - 2
src/layout/index.vue

@@ -1,11 +1,21 @@
 <script setup lang="ts" name="Layout">
 import tabbar from '@/router/modules/tabbar'
-
 const route = useRoute()
 const router = useRouter()
 const active = ref(0)
 const hideHead = computed(() => route.meta.hideHead)
 const title = computed(() => route.meta.title as string)
+const tabbars = ref([])
+setTimeout(()=>{
+  tabbars.value = tabbar
+  //来自SAAS_mob平台去除我的
+  if(localStorage.getItem('formMob')){
+    if(tabbar.length == 3){
+      tabbar.pop()
+      tabbars.value = tabbar
+    }
+  }
+})
 const onBack = () => {
   router.back()
 }
@@ -28,7 +38,7 @@ const onBack = () => {
     </router-view>
     <van-tabbar route placeholder fixed v-model="active">
       <van-tabbar-item
-        v-for="item in tabbar"
+        v-for="item in tabbars"
         :key="item.path"
         :icon="item.meta?.icon as string"
         replace

+ 27 - 24
src/router/index.ts

@@ -3,7 +3,7 @@ import NProgress from '@/config/progress'
 import index from './modules'
 import { useAuthStore } from '@/stores/modules/auth'
 import { useCachedViewStore } from '@/stores/modules/cachedView'
-
+import { getQueryString } from '@/utils'
 const router = createRouter({
   history: createWebHistory(import.meta.env.VITE_PUBLIC_PATH),
   scrollBehavior: () => ({ left: 0, top: 0 }),
@@ -24,33 +24,36 @@ router.beforeEach(async (to, from, next) => {
   // 动态设置标题
   const title = import.meta.env.VITE_GLOB_APP_TITLE
   document.title = to.meta.title ? `${to.meta.title} - ${title}` : title
-
-  // 是否为跳转登录页
-  if (to.path.toLocaleLowerCase() === '/login') {
-    if (authStore.tokenGet) {
-      // 哪来回哪去
-      return next(from.fullPath)
+  const token = getQueryString(window.location.href,"token")
+  if(token){
+      next()
+  }else{
+      // 是否为跳转登录页
+    if (to.path.toLocaleLowerCase() === '/login') {
+      if (authStore.tokenGet) {
+        // 哪来回哪去
+        return next(from.fullPath)
+      }
+      return next()
     }
-    return next()
-  }
 
-  // 白名单中的路由直接放行
-  if (whileList.includes(to.path)) {
-    return next()
-  }
+    // 白名单中的路由直接放行
+    if (whileList.includes(to.path)) {
+      return next()
+    }
 
-  // 不携带token的情况下,跳转到登录页
-  if (!authStore.tokenGet) {
-    next({ path: `/login?redirect=${to.fullPath}`, replace: true })
-    return
-  }
+    // 不携带token的情况下,跳转到登录页
+    if (!authStore.tokenGet) {
+      next({ path: `/login?redirect=${to.fullPath}`, replace: true })
+      return
+    }
 
-  // 获取用户信息
-  if (!authStore.usernameGet) {
-    await authStore.getInfo()
+    // 获取用户信息
+    if (!authStore.usernameGet) {
+      await authStore.getInfo()
+    }
+    next()
   }
-
-  next()
 })
 
 router.afterEach(() => {
@@ -61,4 +64,4 @@ router.onError(() => {
   NProgress.done()
 })
 
-export default router
+export default router

+ 2 - 3
src/router/modules/tabbar.ts

@@ -2,7 +2,6 @@ import type { RouteRecordRaw } from 'vue-router'
 import Dashboard from '@/views/dashboard/index.vue'
 import Profile from '@/views/profile/index.vue'
 import Workbench from '@/views/workbench/index.vue'
-
 const routes: Array<RouteRecordRaw> = [
   {
     path: '/dashboard',
@@ -32,6 +31,6 @@ const routes: Array<RouteRecordRaw> = [
       hideHead: true
     }
   }
-]
 
-export default routes
+]
+export default routes

+ 11 - 2
src/stores/modules/auth.ts

@@ -1,6 +1,6 @@
 import { defineStore } from 'pinia'
-import type { LoginForm, UserInfo } from '@/api/modules/oauth/auth'
-import { getInfo, login, logout, refreshToken } from '@/api/modules/oauth/auth'
+import type { LoginForm, loginForm2, UserInfo } from '@/api/modules/oauth/auth'
+import { getInfo, login, logout, saltLogin, refreshToken } from '@/api/modules/oauth/auth'
 import router from '@/router'
 
 interface AuthState {
@@ -34,6 +34,15 @@ export const useAuthStore = defineStore({
         return res.data
       }
     },
+    async saltLogin(loginForm2: LoginForm2) {
+      const res = await saltLogin(loginForm2)
+      if (res.success) {
+        const { accessToken, refreshToken } = res.data
+        this.accessToken = accessToken
+        this.refreshToken = refreshToken
+        return res.data
+      }
+    },
     async logout() {
       try {
         await logout()

+ 58 - 70
src/typings/auto-imports.d.ts

@@ -5,79 +5,67 @@
 // Generated by unplugin-auto-import
 export {}
 declare global {
-  const EffectScope: (typeof import('vue'))['EffectScope']
-  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 effectScope: (typeof import('vue'))['effectScope']
-  const getCurrentInstance: (typeof import('vue'))['getCurrentInstance']
-  const getCurrentScope: (typeof import('vue'))['getCurrentScope']
-  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 onBeforeRouteLeave: (typeof import('vue-router'))['onBeforeRouteLeave']
-  const onBeforeRouteUpdate: (typeof import('vue-router'))['onBeforeRouteUpdate']
-  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 provide: (typeof import('vue'))['provide']
-  const reactive: (typeof import('vue'))['reactive']
-  const readonly: (typeof import('vue'))['readonly']
-  const ref: (typeof import('vue'))['ref']
-  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 toValue: (typeof import('vue'))['toValue']
-  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 useLink: (typeof import('vue-router'))['useLink']
-  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']
+  const EffectScope: typeof import('vue')['EffectScope']
+  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 effectScope: typeof import('vue')['effectScope']
+  const getCurrentInstance: typeof import('vue')['getCurrentInstance']
+  const getCurrentScope: typeof import('vue')['getCurrentScope']
+  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 onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
+  const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
+  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 provide: typeof import('vue')['provide']
+  const reactive: typeof import('vue')['reactive']
+  const readonly: typeof import('vue')['readonly']
+  const ref: typeof import('vue')['ref']
+  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 toValue: typeof import('vue')['toValue']
+  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 useLink: typeof import('vue-router')['useLink']
+  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 type re-export
 declare global {
   // @ts-ignore
-  export type {
-    Component,
-    ComponentPublicInstance,
-    ComputedRef,
-    ExtractDefaultPropTypes,
-    ExtractPropTypes,
-    ExtractPublicPropTypes,
-    InjectionKey,
-    PropType,
-    Ref,
-    VNode,
-    WritableComputedRef
-  } from 'vue'
+  export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
   import('vue')
 }

+ 7 - 1
src/typings/components.d.ts

@@ -38,10 +38,11 @@ declare module 'vue' {
     VanCellGroup: typeof import('vant/es')['CellGroup']
     VanCheckbox: typeof import('vant/es')['Checkbox']
     VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup']
-    VanCol: typeof import('vant/es')['Col']
     VanCollapse: typeof import('vant/es')['Collapse']
     VanCollapseItem: typeof import('vant/es')['CollapseItem']
     VanConfigProvider: typeof import('vant/es')['ConfigProvider']
+    VanDatePicker: typeof import('vant/es')['DatePicker']
+    VanDivider: typeof import('vant/es')['Divider']
     VanField: typeof import('vant/es')['Field']
     VanForm: typeof import('vant/es')['Form']
     VanIcon: typeof import('vant/es')['Icon']
@@ -49,20 +50,25 @@ declare module 'vue' {
     VanList: typeof import('vant/es')['List']
     VanNavBar: typeof import('vant/es')['NavBar']
     VanPicker: typeof import('vant/es')['Picker']
+    VanPickerGroup: typeof import('vant/es')['PickerGroup']
     VanPopup: typeof import('vant/es')['Popup']
     VanPullRefresh: typeof import('vant/es')['PullRefresh']
     VanRadio: typeof import('vant/es')['Radio']
     VanRadioGroup: typeof import('vant/es')['RadioGroup']
+    VanRate: typeof import('vant/es')['Rate']
     VanRow: typeof import('vant/es')['Row']
     VanSearch: typeof import('vant/es')['Search']
     VanSkeleton: typeof import('vant/es')['Skeleton']
+    VanSlider: typeof import('vant/es')['Slider']
     VanSpace: typeof import('vant/es')['Space']
     VanStepper: typeof import('vant/es')['Stepper']
+    VanSwitch: typeof import('vant/es')['Switch']
     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']
+    VanTimePicker: typeof import('vant/es')['TimePicker']
     VanUploader: typeof import('vant/es')['Uploader']
   }
 }

+ 11 - 0
src/utils/index.ts

@@ -23,3 +23,14 @@ export function flatNodes(nodes: FlowNode) {
   loop(nodes)
   return all
 }
+/**
+ * url参数获取
+ * @param url,
+ * @param name
+ */
+export function getQueryString(url, name) {
+  // 构造正则表达式,匹配 ? 或 & 后面跟随的参数名称和其值
+  const regex = new RegExp('[?&]' + name + '=([^&#]*)', 'i');
+  const result = regex.exec(url);
+  return result ? decodeURIComponent(result[1]) : null;
+}

+ 44 - 31
src/views/dashboard/index.vue

@@ -38,38 +38,41 @@ onMounted(() => {
 
 <template>
   <van-search v-model="searchText" placeholder="请输入流程关键词" />
-  <van-collapse v-model="activeNames">
-    <van-collapse-item
-      v-for="item in defineGroup"
-      :key="item.id"
-      :title="item.name"
-      :name="item.id"
-    >
-      <template #title>
-        <span class="group-title">
-          {{ item.name }}
-        </span>
-        <span>({{ item.defines?.length || 0 }})</span>
-      </template>
-      <van-space wrap :size="25">
-        <div
-          v-for="define in item.defines"
-          :key="define.id"
-          class="flow-box flex-col-center"
-          @click="handleStartProcess(define)"
-        >
+  <van-collapse v-model="activeNames" class="dashboard">
+    <div v-for="item in defineGroup" :key="item.id">
+      <van-collapse-item
+        :title="item.name"
+        :name="item.id"
+        style="  margin-right:0 !important;"
+        v-if="item.defines?.length"
+      >
+        <template #title>
+          <span class="group-title">
+            {{ item.name }}
+          </span>
+          <span>({{ item.defines?.length || 0 }})</span>
+        </template>
+        <van-space wrap :size="25" >
           <div
-            class="flow-icon"
-            :style="{ background: define.icon.bgColor, color: define.icon.color }"
+            v-for="define in item.defines"
+            :key="define.id"
+            class="flow-box flex-col-center"
+            @click="handleStartProcess(define)"
+            style="text-align:center;padding-left:35px;"
           >
-            <div :class="define.icon.name"></div>
+            <div
+              class="flow-icon"
+              :style="{ background: define.icon.bgColor, color: define.icon.color }"
+            >
+              <div :class="define.icon.name"></div>
+            </div>
+            <span class="flow-text">
+              {{ define.name }}
+            </span>
           </div>
-          <span class="flow-text">
-            {{ define.name }}
-          </span>
-        </div>
-      </van-space>
-    </van-collapse-item>
+        </van-space>
+      </van-collapse-item>
+    </div>
   </van-collapse>
 </template>
 
@@ -79,20 +82,30 @@ onMounted(() => {
   padding-left: 5px;
 }
 .flow-box {
+  width:20% !important;
+  // margin-left:3%;
   .flow-icon {
     padding: 8px;
     border-radius: 10px;
-
     div {
       font-size: 25px;
     }
   }
-
   .flow-text {
     max-width: 65px;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
+    font-size: 12px !important;
+    margin-top:4px
+  }
+}
+</style>
+<style lang="less">
+.dashboard{
+  .van-space-item{
+    width:15% !important;
+    margin-left:10px !important;
   }
 }
 </style>

+ 20 - 5
src/views/login/index.vue

@@ -2,13 +2,14 @@
 import { getCodeImg, type LoginForm, type VerificationCode } from '@/api/modules/oauth/auth'
 import { type FormInstance, showLoadingToast, showSuccessToast } from 'vant'
 import { useAuthStore } from '@/stores/modules/auth'
-
+import { useRouter } from 'vue-router'
+import { getQueryString } from '@/utils'
 const router = useRouter()
 const authStore = useAuthStore()
 const title = computed(() => import.meta.env.VITE_GLOB_APP_TITLE)
 const loginForm = ref<LoginForm>({
-  username: 'liyuzhen',
-  password: '123456',
+  username: '',
+  password: '',
   code: '',
   uuid: ''
 })
@@ -19,8 +20,22 @@ const verificationCode = ref<VerificationCode>({
   img: '',
   uuid: ''
 })
+const pageStatus = ref(false)//页面状态
 onMounted(() => {
-  getVerificationCode()
+  const token = getQueryString(window.location.href,"token")
+  if (token) {
+    authStore.saltLogin({ salt: token?.includes('?') ? token.split('?')[0] : token }).then(() => {
+      const url = getQueryString(window.location.href,"urlF")
+      if (url) {
+        localStorage.setItem('formMob', url)
+      }
+      const { redirect } = router.currentRoute.value.query
+      router.replace((redirect as string) || '/')
+    })
+  }else{
+    pageStatus.value = true
+    getVerificationCode()
+  }
 })
 const handleSubmit = () => {
   formRef.value?.validate().then(async () => {
@@ -50,7 +65,7 @@ const getVerificationCode = () => {
 </script>
 
 <template>
-  <div>
+  <div v-if="pageStatus">
     <div class="h-screen flex justify-center p-8">
       <div class="flex-col">
         <!--标题-->

+ 3 - 19
src/views/profile/index.vue

@@ -34,16 +34,15 @@ const toggle = () => {
       <button class="i-carbon-sun dark:i-carbon-moon" @click="toggle()" />
     </template>
   </van-nav-bar>
-
   <van-space direction="vertical" :size="15" fill>
     <div class="flex-between mx-5 mt5">
       <div class="flex-items-center">
         <van-image class="h-12 w-12" round fit="cover" :src="authStore.avatarGet" />
         <span class="ml-2 text-4 font-black">{{ authStore.nameGet }}</span>
       </div>
-      <van-icon name="arrow" />
+      <!-- <van-icon name="arrow" /> -->
     </div>
-    <van-cell-group inset>
+    <!-- <van-cell-group inset>
       <van-row justify="space-around" class="p3">
         <van-col class="flex-col items-center" span="6">
           <span class="statistics-title">待办</span>
@@ -58,23 +57,8 @@ const toggle = () => {
           <span class="statistics-count">78</span>
         </van-col>
       </van-row>
-    </van-cell-group>
+    </van-cell-group> -->
     <van-cell-group inset>
-      <van-cell :border="false" title="个人信息" is-link>
-        <template #icon>
-          <i class="i-mingcute:idcard-fill mr-2 text-xl" />
-        </template>
-      </van-cell>
-      <van-cell :border="false" title="账号与安全" is-link>
-        <template #icon>
-          <i class="i-material-symbols:account-box mr-2 text-xl" />
-        </template>
-      </van-cell>
-      <van-cell :border="false" title="主题设置" is-link>
-        <template #icon>
-          <i class="i-ant-design:setting-filled mr-2 text-xl" />
-        </template>
-      </van-cell>
       <van-cell :border="false" title="退出登录" is-link @click="showLogoutAction = true">
         <template #icon>
           <i class="i-solar:logout-3-bold mr-2 text-xl" />

+ 49 - 3
src/views/startProcess/index.vue

@@ -4,7 +4,7 @@ import { type FormInfo, getStartForm, start, type StartProcess } from '@/api/mod
 import { showFailToast, showSuccessToast } from 'vant'
 import ProcessTimeline from './timeline/index.vue'
 import { getInstance, getVariables } from '@/api/modules/flow/instance'
-
+import { throttle } from 'lodash-es'
 const router = useRouter()
 const route = router.currentRoute
 const formInfoLoading = ref(true)
@@ -69,10 +69,40 @@ const formParserRef = ref<InstanceType<typeof FormParser>>()
 const onBack = () => {
   router.back()
 }
-const onsubmit = () => {
+const onsubmit = throttle(
+  () => {
   const formValidate = formParserRef.value?.submitForm()
   const timelineValidate = processTimelineRef.value?.submitForm()
-  Promise.all([formValidate, timelineValidate]).then(() => {
+  // 处理上传文件路径
+    var timelineValidateChange ={}
+    timelineValidate.then((res)=>{
+      var values = Object.values(res)
+      var keys = Object.keys(res) 
+      for (var i = 0; i < values.length; i++) {
+        if (Array.isArray(values[i])) {
+          for (var j = 0; j < values[i].length; j++) {
+            if (values[i][j]?.url?.includes(import.meta.env.VITE_IMAGE_URL)) {
+              values[i][j].url = values[i][j].url.replace(import.meta.env.VITE_IMAGE_URL, '')
+            }
+          }
+        }
+      }
+      keys.forEach((key, index) => {
+          res[key] = values[index];
+      });
+      timelineValidateChange = res
+    })
+  Promise.all([formValidate, timelineValidateChange]).then(() => {
+     //验证节点是否都有选择
+    let data = startProcess.value.values
+    let key = Object.keys(data)
+    for (let i = 0; i < key.length; i++) {
+      if (key[i].includes('node_')) {
+        if (!data[key[i]]) {
+          return false
+        }
+      }
+    }
     start(startProcess.value).then((res) => {
       if (res.success) {
         showSuccessToast('流程发起成功')
@@ -80,6 +110,21 @@ const onsubmit = () => {
       }
     })
   })
+  },
+  2000,
+  { trailing: false }
+)
+function findValue(obj, key) {
+  for (let k in obj) {
+      if (k === key) {
+        if(obj[k].assigneeType == "choice"){
+          startProcess.value.values[obj[k].id + 'Collection'] = ""
+          if(obj[k][k]){
+            findValue(obj[k],"next")
+          }
+        }
+      } 
+  }
 }
 onMounted(() => {
   const { id, instanceId } = route.value.query
@@ -90,6 +135,7 @@ onMounted(() => {
   getStartForm(id as string).then((res) => {
     if (res.success) {
       formInfo.value = res.data
+      findValue(formInfo.value.flowDefine.process,'next')
       formKey.value = Date.now()
       formInfoLoading.value = false
       const { formName, flowDefine } = formInfo.value

+ 3 - 2
src/views/workbench/activityTimeline/Approval.vue

@@ -19,6 +19,7 @@ const userInfo = ref<{
   avatar: '',
   name: ''
 })
+const imageURL = import.meta.env.VITE_IMAGE_URL
 const comments = ref<FlowComment[]>([])
 const commentLoading = ref(false)
 onMounted(() => {
@@ -59,9 +60,9 @@ onMounted(() => {
           <van-image
             v-for="(image, i) in item.attachments.filter((a) => a.type.includes('image'))"
             :key="i"
-            :preview-src-list="[image.url]"
+            :preview-src-list="[imageURL + image.url]"
             style="width: auto; height: 60px"
-            :src="image.url"
+            :src="imageURL + image.url"
           />
         </div>
         <div class="file-comment" v-if="item.attachments">

+ 8 - 6
src/views/workbench/cc.vue

@@ -18,7 +18,7 @@ const queryForm = ref<FlowInstanceQuery>({
   modelCode: '',
   definitionId: '',
   status: undefined,
-  page: 1,
+  page: 0,
   limit: 10
 })
 const onSearch = () => {
@@ -62,16 +62,16 @@ const handleDetails = (cc: FlowInstance) => {
   <form action="/">
     <van-search
       v-model="queryForm.name"
-      show-action
-      placeholder="请输入搜索关键词"
+      placeholder="请输入流程名称"
+      shape="round"
       @search="onSearch"
     >
-      <template #action>
+      <!-- <template #action>
         <div>
           <van-icon name="filter-o" />
           筛选
         </div>
-      </template>
+      </template> -->
     </van-search>
   </form>
   <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
@@ -87,7 +87,9 @@ const handleDetails = (cc: FlowInstance) => {
         <van-cell @click="handleDetails(item)">
           <template #title>
             <div flex-between>
-              {{ item.name }}
+              <div style="width: 85%;">
+                {{ item.name }} 
+              </div>
               <StatusTag :status="item.status" />
             </div>
           </template>

+ 5 - 9
src/views/workbench/done.vue

@@ -64,16 +64,10 @@ const handleTask = (task: FlowTask) => {
   <form action="/">
     <van-search
       v-model="queryForm.instanceName"
-      show-action
-      placeholder="请输入搜索关键词"
+      placeholder="请输入流程名称"
+      shape="round"
       @search="onSearch"
     >
-      <template #action>
-        <div>
-          <van-icon name="filter-o" />
-          筛选
-        </div>
-      </template>
     </van-search>
   </form>
   <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
@@ -89,7 +83,9 @@ const handleTask = (task: FlowTask) => {
         <van-cell @click="handleTask(item)">
           <template #title>
             <div flex-between>
-              {{ item.instanceName }}
+              <div style="width: 85%;">
+                {{ item.instanceName }} 
+              </div>
               <StatusTag :status="item.status" />
             </div>
           </template>

+ 2 - 1
src/views/workbench/handle/Popup.vue

@@ -12,6 +12,7 @@ const onSubmit = () => {}
 const onClose = () => {
   fileList.value = []
   formRef.value?.resetValidation()
+  window.history.back();
 }
 const beforeRead = (
   file: File | File[]
@@ -25,7 +26,7 @@ const beforeRead = (
         type: f.type,
         name: f.name,
         size: f.size,
-        url: `/api${r.data.url}`
+        url: `${r.data.url}`
       }
     })
   })

+ 8 - 6
src/views/workbench/instance.vue

@@ -64,16 +64,16 @@ const handleDetails = (instance: FlowInstance) => {
   <form action="/">
     <van-search
       v-model="queryForm.name"
-      show-action
-      placeholder="请输入搜索关键词"
+      placeholder="请输入流程名称"
+      shape="round"
       @search="onSearch"
     >
-      <template #action>
+      <!-- <template #action>
         <div>
-          <van-icon name="filter-o" />
+          <van-icon name="filter-o" /> 
           筛选
         </div>
-      </template>
+      </template> -->
     </van-search>
   </form>
   <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
@@ -89,7 +89,9 @@ const handleDetails = (instance: FlowInstance) => {
         <van-cell @click="handleDetails(item)">
           <template #title>
             <div flex-between>
-              {{ item.name }}
+              <div style="width: 85%;">
+                {{ item.name }} 
+              </div>
               <StatusTag :status="item.status" />
             </div>
           </template>

+ 8 - 5
src/views/workbench/todo.vue

@@ -33,6 +33,7 @@ const onRefresh = () => {
   onLoad()
 }
 const onLoad = () => {
+  console.log(111)
   queryForm.value.page++
   if (refreshing.value) {
     queryForm.value.page = 1
@@ -64,16 +65,16 @@ const handleTask = (task: FlowTask) => {
   <form action="/">
     <van-search
       v-model="queryForm.instanceName"
-      show-action
-      placeholder="请输入搜索关键词"
+      placeholder="请输入流程名称"
+      shape="round"
       @search="onSearch"
     >
-      <template #action>
+      <!-- <template #action>
         <div>
           <van-icon name="filter-o" />
           筛选
         </div>
-      </template>
+      </template> -->
     </van-search>
   </form>
   <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
@@ -89,7 +90,9 @@ const handleTask = (task: FlowTask) => {
         <van-cell @click="handleTask(item)">
           <template #title>
             <div flex-between>
-              {{ item.instanceName }}
+              <div style="width: 85%;">
+                {{ item.instanceName }} 
+              </div>
               <StatusTag :status="item.status" />
             </div>
           </template>

+ 5 - 5
vite.config.ts

@@ -1,5 +1,5 @@
 import { fileURLToPath, URL } from 'node:url'
-import { defineConfig } from 'vite'
+import { defineConfig, loadEnv } from 'vite'
 import Unocss from 'unocss/vite'
 import vue from '@vitejs/plugin-vue'
 import vueJsx from '@vitejs/plugin-vue-jsx'
@@ -11,8 +11,7 @@ import Components from 'unplugin-vue-components/vite'
 import { VantResolver } from 'unplugin-vue-components/resolvers'
 import { resolve } from 'path'
 import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
-
-// https://vitejs.dev/config/
+const env = process.env.NODE_ENV;
 export default defineConfig({
   plugins: [
     vue(),
@@ -61,14 +60,15 @@ export default defineConfig({
       '@': fileURLToPath(new URL('./src', import.meta.url))
     }
   },
+  base:env === 'production' ? '/mobile' : '/',
   server: {
     host: '0.0.0.0',
     port: 3200,
     open: true,
     proxy: {
       '/api': {
-        // target: 'http://localhost:9089',
-        target: 'https://demo.lowflow.vip/api',
+        target: 'http://localhost:3000',
+        // target: 'https://demo.lowflow.vip/api',
         changeOrigin: true,
         ws: true,
         rewrite: (path) => path.replace(/^\/api/, ''),