u
This commit is contained in:
1 parent
a2c6812ed0
commit
f431b7ead6
17 files changed
+728
-39
No files matched your search
@@ -24,6 +24,20 @@ const props = defineProps({
|
||||
},
|
||||
viewname: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
searchcondition: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
userid: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
searchType: {
|
||||
type: String as PropType<'default' | 'ex' | 'single'>,
|
||||
default: 'default',
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import type { PropType } from 'vue'
|
||||
import { useIndeterminate } from 'sard-uniapp'
|
||||
import { computed, ref } from 'vue'
|
||||
import { getAutocompleteDataApi, getAutocompleteDataExApi, getAutocompleteDataSingleApi } from '@/http/service'
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
@@ -11,10 +13,6 @@ const props = defineProps({
|
||||
type: String as PropType<'radio' | 'multiple'>,
|
||||
default: 'radio',
|
||||
},
|
||||
label: {
|
||||
value: String,
|
||||
default: '',
|
||||
},
|
||||
value: {
|
||||
type: null as unknown as PropType<any | any[]>,
|
||||
default: undefined,
|
||||
@@ -30,8 +28,21 @@ const props = defineProps({
|
||||
viewname: {
|
||||
type: String,
|
||||
},
|
||||
searchcondition: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
userid: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
searchType: {
|
||||
type: String as PropType<'default' | 'ex' | 'single'>,
|
||||
default: 'default',
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['update:visible', 'update:value'])
|
||||
const emit = defineEmits(['update:visible', 'update:value', 'change'])
|
||||
|
||||
const _visible = computed({
|
||||
get: () => props.visible,
|
||||
@@ -43,34 +54,35 @@ const _visible = computed({
|
||||
const _loading = ref(false)
|
||||
const searchValue = ref('')
|
||||
|
||||
const _pagation = ref({
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 20,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const isPrevDisabled = computed(() => _pagation.value.current <= 1)
|
||||
const isPrevDisabled = computed(() => pagination.value.current <= 1)
|
||||
const isNextDisabled = computed(() =>
|
||||
_pagation.value.current >= Math.ceil(_pagation.value.total / _pagation.value.pageSize),
|
||||
pagination.value.current >= Math.ceil(pagination.value.total / pagination.value.pageSize),
|
||||
)
|
||||
|
||||
// 内部 value,双向绑定 props.value
|
||||
const _value = computed({
|
||||
get: () => {
|
||||
// 如果是多选,保证返回数组
|
||||
return props.type === 'multiple'
|
||||
? (Array.isArray(props.value) ? [...props.value] : [])
|
||||
: props.value
|
||||
if (props.type === 'multiple') {
|
||||
// 只在不是数组时才兜底成 []
|
||||
return Array.isArray(props.value) ? props.value : []
|
||||
}
|
||||
return props.value
|
||||
},
|
||||
set: (val) => {
|
||||
emit('update:value', val)
|
||||
},
|
||||
})
|
||||
|
||||
const _options = ref<any[]>([])
|
||||
const options = ref<any[]>([])
|
||||
|
||||
function setOptions(val: any[]) {
|
||||
_options.value = val.map(item => ({
|
||||
options.value = val.map(item => ({
|
||||
label: item[props.labelField] ?? '',
|
||||
value: item[props.valueField] ?? undefined,
|
||||
disabled: !!item.disabled,
|
||||
@@ -80,7 +92,7 @@ function setOptions(val: any[]) {
|
||||
|
||||
const { allChecked, isIndeterminate, onAllChange } = useIndeterminate(
|
||||
_value,
|
||||
_options,
|
||||
options,
|
||||
)
|
||||
|
||||
function select(option, toggle) {
|
||||
@@ -88,6 +100,14 @@ function select(option, toggle) {
|
||||
toggle(option.value)
|
||||
if (props.type === 'radio') {
|
||||
_visible.value = false
|
||||
|
||||
// 查找对应的 label
|
||||
const selectedOption = options.value.find(
|
||||
o => o.value === option.value,
|
||||
)
|
||||
|
||||
// 触发事件或者更新内部 _label
|
||||
emit('change', { selectedOption })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,19 +115,95 @@ function select(option, toggle) {
|
||||
// 显示数量
|
||||
const count = computed(() => {
|
||||
if (props.type === 'multiple') {
|
||||
return `${(_value.value).length || 0} / ${_options.value.length}`
|
||||
return `${(_value.value).length || 0} / ${options.value.length}`
|
||||
}
|
||||
else {
|
||||
return _value.value != null ? `1 / ${_options.value.length}` : `0 / ${_options.value.length}`
|
||||
return _value.value != null ? `1 / ${options.value.length}` : `0 / ${options.value.length}`
|
||||
}
|
||||
})
|
||||
|
||||
function handleAfterEnter() {
|
||||
// _loading.value = true
|
||||
getData()
|
||||
}
|
||||
|
||||
function handlePagationChange(page: number) {
|
||||
_pagation.value.current = page
|
||||
pagination.value.current = page
|
||||
|
||||
getData()
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
pagination.value.current = 1
|
||||
pagination.value.total = 0
|
||||
pagination.value.pageSize = 10
|
||||
|
||||
getData()
|
||||
}
|
||||
|
||||
function getData() {
|
||||
if (props.searchType === 'ex') {
|
||||
getAutocompleteDataEx()
|
||||
}
|
||||
else if (props.searchType === 'single') {
|
||||
getAutocompleteDataSingle()
|
||||
}
|
||||
else {
|
||||
getAutocompleteData()
|
||||
}
|
||||
}
|
||||
|
||||
function getAutocompleteData() {
|
||||
_loading.value = true
|
||||
getAutocompleteDataApi({
|
||||
q: searchValue.value,
|
||||
page: pagination.value.current,
|
||||
limit: pagination.value.pageSize,
|
||||
viewname: props.viewname,
|
||||
field: props.labelField,
|
||||
}).then((res) => {
|
||||
pagination.value.total = res.data.total
|
||||
|
||||
setOptions(res.data.rows)
|
||||
}).finally(() => {
|
||||
_loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function getAutocompleteDataEx() {
|
||||
_loading.value = true
|
||||
getAutocompleteDataExApi({
|
||||
q: searchValue.value,
|
||||
page: pagination.value.current,
|
||||
limit: pagination.value.pageSize,
|
||||
viewname: props.viewname,
|
||||
field: props.labelField,
|
||||
userid: props.userid,
|
||||
searchcondition: props.searchcondition,
|
||||
}).then((res) => {
|
||||
pagination.value.total = res.data.total
|
||||
|
||||
setOptions(res.data.rows)
|
||||
}).finally(() => {
|
||||
_loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function getAutocompleteDataSingle() {
|
||||
_loading.value = true
|
||||
getAutocompleteDataSingleApi({
|
||||
q: searchValue.value,
|
||||
page: pagination.value.current,
|
||||
limit: pagination.value.pageSize,
|
||||
viewname: props.viewname,
|
||||
field: props.labelField,
|
||||
userid: props.userid,
|
||||
}).then((res) => {
|
||||
pagination.value.total = res.data.total
|
||||
|
||||
setOptions(res.data.rows)
|
||||
}).finally(() => {
|
||||
_loading.value = false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -137,6 +233,7 @@ function handlePagationChange(page: number) {
|
||||
v-model="searchValue"
|
||||
placeholder="请输入搜索的值"
|
||||
clearable
|
||||
@confirm="handleSearch"
|
||||
>
|
||||
<template #prepend>
|
||||
<sar-icon family="sari" name="search" />
|
||||
@@ -154,7 +251,7 @@ function handlePagationChange(page: number) {
|
||||
:throttle="false"
|
||||
>
|
||||
<sar-empty
|
||||
v-if="_options.length === 0"
|
||||
v-if="options.length === 0"
|
||||
:root-style="{ height: '100%' }"
|
||||
/>
|
||||
|
||||
@@ -166,7 +263,7 @@ function handlePagationChange(page: number) {
|
||||
<template #custom="{ toggle }">
|
||||
<sar-list inlaid>
|
||||
<sar-list-item
|
||||
v-for="option in _options"
|
||||
v-for="option in options"
|
||||
:key="option.value"
|
||||
:title="option.label"
|
||||
@click="select(option, toggle)"
|
||||
@@ -191,7 +288,7 @@ function handlePagationChange(page: number) {
|
||||
<template #custom="{ toggle }">
|
||||
<sar-list inlaid>
|
||||
<sar-list-item
|
||||
v-for="option in _options"
|
||||
v-for="option in options"
|
||||
:key="option.value"
|
||||
:title="option.label"
|
||||
:hover="!option.disabled"
|
||||
@@ -213,9 +310,9 @@ function handlePagationChange(page: number) {
|
||||
</view>
|
||||
<view class="sar-popout__footer">
|
||||
<sar-pagination
|
||||
:current="_pagation.current"
|
||||
:total="_pagation.total"
|
||||
:page-size="_pagation.pageSize"
|
||||
:current="pagination.current"
|
||||
:total="pagination.total"
|
||||
:page-size="pagination.pageSize"
|
||||
type="simple"
|
||||
root-class="picker-select__pagination-root"
|
||||
@change="handlePagationChange"
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
import type { CustomRequestOptions } from './interceptor'
|
||||
import { toast } from '@/utils/toast'
|
||||
import { httpInterceptor } from './interceptor'
|
||||
|
||||
export function http<T>(options: CustomRequestOptions): Promise<IResData<T>> {
|
||||
return httpInternal<T>(options)
|
||||
}
|
||||
|
||||
async function httpInternal<T>(options: CustomRequestOptions): Promise<IResData<T>> {
|
||||
try {
|
||||
await httpInterceptor.invoke(options)
|
||||
}
|
||||
catch (e) {
|
||||
console.log('异常')
|
||||
return Promise.reject(e)
|
||||
}
|
||||
|
||||
return new Promise<IResData<T>>((resolve, reject) => {
|
||||
uni.request({
|
||||
...options,
|
||||
dataType: 'json',
|
||||
// #ifndef MP-WEIXIN
|
||||
responseType: 'json',
|
||||
// #endif
|
||||
success(res) {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
const resData = res.data as IResData<T>
|
||||
if (resData.code === 200) {
|
||||
try {
|
||||
resData.data = JSON.parse(resData.data as string)
|
||||
}
|
||||
catch {}
|
||||
resolve(resData)
|
||||
}
|
||||
else if (resData.code === 600) {
|
||||
toast.error(resData.message || '当前登录已过期')
|
||||
uni.navigateTo({ url: '/pages-sub/login/index' })
|
||||
reject(res)
|
||||
}
|
||||
else {
|
||||
toast.error(resData.message || '请求错误')
|
||||
reject(res)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!options.hideErrorToast) {
|
||||
toast.error((res.data as IResData<T>).message || '请求错误')
|
||||
}
|
||||
reject(res)
|
||||
}
|
||||
},
|
||||
fail(err) {
|
||||
toast.error('网络错误,换个网络试试')
|
||||
reject(err)
|
||||
},
|
||||
complete() {
|
||||
console.log('complete')
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* GET 请求
|
||||
* @param url 后台地址
|
||||
* @param query 请求query参数
|
||||
* @returns
|
||||
*/
|
||||
export function httpGet<T>(url: string, query?: Record<string, any>) {
|
||||
return http<T>({
|
||||
url,
|
||||
query,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 请求
|
||||
* @param url 后台地址
|
||||
* @param data 请求body参数
|
||||
* @param query 请求query参数,post请求也支持query,很多微信接口都需要
|
||||
* @returns
|
||||
*/
|
||||
export function httpPostForm<T>(
|
||||
url: string,
|
||||
data?: Record<string, any>,
|
||||
query?: Record<string, any>,
|
||||
) {
|
||||
return http<T>({
|
||||
url,
|
||||
query,
|
||||
data,
|
||||
method: 'POST',
|
||||
header: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
})
|
||||
}
|
||||
export function httpPostJson<T>(
|
||||
url: string,
|
||||
data?: Record<string, any>,
|
||||
query?: Record<string, any>,
|
||||
) {
|
||||
return http<T>({
|
||||
url,
|
||||
query,
|
||||
data,
|
||||
method: 'POST',
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
http.get = httpGet
|
||||
http.postForm = httpPostForm
|
||||
http.postJson = httpPostJson
|
||||
@@ -0,0 +1,85 @@
|
||||
import { useUserStore } from '@/store'
|
||||
import { joinUrl } from '@/utils/common'
|
||||
import { getDbConfigs } from '@/utils/request'
|
||||
import { toast } from '@/utils/toast'
|
||||
import { stringifyQuery } from './queryString'
|
||||
|
||||
export type CustomRequestOptions = UniApp.RequestOptions & {
|
||||
query?: Record<string, any>
|
||||
/** 出错时是否隐藏错误提示 */
|
||||
hideErrorToast?: boolean
|
||||
} & IUniUploadFileOptions // 添加uni.uploadFile参数类型
|
||||
|
||||
export const httpInterceptor = {
|
||||
async invoke(options: CustomRequestOptions) {
|
||||
// 1. 处理 query 参数
|
||||
if (options.query) {
|
||||
const queryStr = stringifyQuery(options.query)
|
||||
options.url += options.url.includes('?') ? `&${queryStr}` : `?${queryStr}`
|
||||
}
|
||||
|
||||
// 2. 获取用户信息
|
||||
const userStore = useUserStore()
|
||||
const userInfo = userStore.userInfo as IUserInfo
|
||||
const { token, userid, orgid } = userInfo || {}
|
||||
const isLogin = options.url.includes('login')
|
||||
|
||||
// 3. 获取 db 配置
|
||||
const configMap = await getDbConfigs()
|
||||
if (!configMap) {
|
||||
toast.error('获取机构配置失败,请联系管理人员!')
|
||||
if (!isLogin) {
|
||||
userStore.reset()
|
||||
uni.navigateTo({ url: '/pages-sub/login/index' })
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('机构码无效'))
|
||||
}
|
||||
console.log(configMap)
|
||||
|
||||
// 4. 解析 baseUrl
|
||||
let baseUrl = ''
|
||||
if (isLogin) {
|
||||
const orgidFromData = (options.data as AnyObject)?.orgid || ''
|
||||
baseUrl = configMap[orgidFromData] || ''
|
||||
}
|
||||
else {
|
||||
baseUrl = configMap[orgid || ''] || ''
|
||||
}
|
||||
|
||||
if (!baseUrl) {
|
||||
toast.error('机构码有误,请重新登陆!')
|
||||
if (!isLogin) {
|
||||
userStore.reset()
|
||||
uni.navigateTo({ url: '/pages-sub/login/index' })
|
||||
}
|
||||
return Promise.reject(new Error('机构码无效'))
|
||||
}
|
||||
|
||||
// 5. 设置请求头
|
||||
if (!isLogin) {
|
||||
options.header = {
|
||||
...options.header,
|
||||
Authorization: `Bearer ${token}`,
|
||||
userid: encodeURIComponent(userid),
|
||||
orgid,
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 拼接完整 URL、设置超时与平台信息
|
||||
options.url = joinUrl(baseUrl, options.url)
|
||||
options.timeout = 20000
|
||||
options.header = {
|
||||
...options.header,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export const requestInterceptor = {
|
||||
install() {
|
||||
// 拦截 request 请求
|
||||
// uni.addInterceptor('request', httpInterceptor)
|
||||
// 拦截 uploadFile 文件上传
|
||||
// uni.addInterceptor('uploadFile', httpInterceptor)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 将对象序列化为URL查询字符串,用于替代第三方的 qs 库,节省宝贵的体积
|
||||
* 支持基本类型值和数组,不支持嵌套对象
|
||||
* @param obj 要序列化的对象
|
||||
* @returns 序列化后的查询字符串
|
||||
*/
|
||||
export function stringifyQuery(obj: Record<string, any>): string {
|
||||
if (!obj || typeof obj !== 'object' || Array.isArray(obj))
|
||||
return ''
|
||||
|
||||
return Object.entries(obj)
|
||||
.filter(([_, value]) => value !== undefined && value !== null)
|
||||
.map(([key, value]) => {
|
||||
// 对键进行编码
|
||||
const encodedKey = encodeURIComponent(key)
|
||||
|
||||
// 处理数组类型
|
||||
if (Array.isArray(value)) {
|
||||
return value
|
||||
.filter(item => item !== undefined && item !== null)
|
||||
.map(item => `${encodedKey}=${encodeURIComponent(item)}`)
|
||||
.join('&')
|
||||
}
|
||||
|
||||
// 处理基本类型
|
||||
return `${encodedKey}=${encodeURIComponent(value)}`
|
||||
})
|
||||
.join('&')
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { httpPostForm } from './index'
|
||||
|
||||
interface getAutocompleteDataParams {
|
||||
q: string
|
||||
page: number
|
||||
limit: number
|
||||
viewname: string
|
||||
field: string
|
||||
}
|
||||
|
||||
export function getAutocompleteDataApi(params: getAutocompleteDataParams) {
|
||||
return httpPostForm<any>('/getAutocompleteData1.jsp', params)
|
||||
}
|
||||
|
||||
interface getAutocompleteDataExParams {
|
||||
q: string
|
||||
page: number
|
||||
limit: number
|
||||
viewname: string
|
||||
field: string
|
||||
userid?: string
|
||||
searchcondition: string
|
||||
}
|
||||
export function getAutocompleteDataExApi(params: getAutocompleteDataExParams) {
|
||||
return httpPostForm<any>('/getAutocompleteDataEx1.jsp', params)
|
||||
}
|
||||
|
||||
interface getAutocompleteDataSingleParams {
|
||||
q: string
|
||||
page: number
|
||||
limit: number
|
||||
viewname: string
|
||||
field: string
|
||||
userid?: string
|
||||
}
|
||||
|
||||
export function getAutocompleteDataSingleApi(params: getAutocompleteDataSingleParams) {
|
||||
return httpPostForm<any>('/getAutocompleteDataSingle.jsp', params)
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { createSSRApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import store from './store'
|
||||
import '@/style/index.scss'
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
app.use(store)
|
||||
|
||||
return {
|
||||
app,
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="g3-container">
|
||||
Container
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.g3-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
padding: 2em;
|
||||
margin: 0 auto;
|
||||
font-family: 'Inter', sans-serif; /* 设置字体为Inter */
|
||||
}
|
||||
.g3-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
.g3-header-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #575dfb;
|
||||
}
|
||||
.g3-header-prompt {
|
||||
font-size: 16px;
|
||||
color: #666; /* 提示文字颜色稍微调淡 */
|
||||
}
|
||||
}
|
||||
.g3-content {
|
||||
width: 100%; /* 宽度调整为100%,以适应容器大小 */
|
||||
.g3-content-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
.g3-content-form-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
.label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
::v-deep .input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.3rem;
|
||||
border: 2px solid #0498e1 !important;
|
||||
border-radius: 6px;
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
.wd-input__icon {
|
||||
font-size: 20px;
|
||||
color: #575dfb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.g3-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
width: 100%; /* 宽度调整为100% */
|
||||
text-align: center;
|
||||
::v-deep .login-button {
|
||||
height: 3rem;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
border-radius: 6px;
|
||||
.wd-button__text {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
.register {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -16,10 +16,12 @@ function show() {
|
||||
</sar-list>
|
||||
<sar-form label-width="240rpx">
|
||||
<sar-form-item label="Activity name">
|
||||
<picker-select-input />
|
||||
</sar-form-item>
|
||||
<sar-form-item label="Activity name">
|
||||
<picker-select-input />
|
||||
<picker-select-input
|
||||
viewname="b_user"
|
||||
label-field="b_name"
|
||||
value-field="b_id"
|
||||
search-type="ex"
|
||||
/>
|
||||
</sar-form-item>
|
||||
</sar-form>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createPinia } from 'pinia'
|
||||
import { createPersistedState } from 'pinia-plugin-persistedstate' // 数据持久化
|
||||
|
||||
const store = createPinia()
|
||||
store.use(
|
||||
createPersistedState({
|
||||
storage: {
|
||||
getItem: uni.getStorageSync,
|
||||
setItem: uni.setStorageSync,
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
export default store
|
||||
|
||||
export * from './user'
|
||||
@@ -0,0 +1,44 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
import { toast } from '@/utils/toast'
|
||||
|
||||
const initState = { token: '', orgid: '', userid: '' }
|
||||
|
||||
export const useUserStore = defineStore(
|
||||
'user',
|
||||
() => {
|
||||
const userInfo = ref<IUserInfo>({ ...initState })
|
||||
|
||||
const setUserInfo = (val: IUserInfo) => {
|
||||
userInfo.value = val
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
userInfo.value = { orgid: userInfo.value.orgid, userid: userInfo.value.userid, token: '' }
|
||||
}
|
||||
|
||||
const isLogined = computed(
|
||||
() => !!(userInfo.value.token && userInfo.value.orgid && userInfo.value.userid),
|
||||
)
|
||||
|
||||
function validateLogin(): boolean {
|
||||
if (!isLogined.value) {
|
||||
toast.error('尚未登陆或登录已过期')
|
||||
uni.navigateTo({ url: '/pages-sub/login/index' })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return {
|
||||
userInfo,
|
||||
setUserInfo,
|
||||
reset,
|
||||
isLogined,
|
||||
validateLogin,
|
||||
}
|
||||
},
|
||||
{
|
||||
persist: true,
|
||||
},
|
||||
)
|
||||
@@ -7,3 +7,7 @@
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
uni-toast {
|
||||
z-index: 9999 !important; /* 根据实际情况进行调整 */
|
||||
}
|
||||
+4
-9
@@ -3,7 +3,7 @@
|
||||
declare global {
|
||||
interface IResData<T> {
|
||||
code: number
|
||||
msg: string
|
||||
message: string
|
||||
data: T
|
||||
}
|
||||
|
||||
@@ -17,16 +17,11 @@ declare global {
|
||||
}
|
||||
|
||||
interface IUserInfo {
|
||||
nickname?: string
|
||||
avatar?: string
|
||||
orgid?: string // 机构码
|
||||
userid?: string // 主键id,登录账号
|
||||
/** 微信的 openid,非微信没有这个字段 */
|
||||
openid?: string
|
||||
}
|
||||
|
||||
interface IUserToken {
|
||||
token: string
|
||||
refreshToken?: string
|
||||
refreshExpire?: number
|
||||
token?: string
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { toast } from './toast'
|
||||
|
||||
export function joinUrl(base: string, path: string): string {
|
||||
if (!base)
|
||||
return path || ''
|
||||
if (!path)
|
||||
return base || ''
|
||||
|
||||
const baseEndsWithSlash = base.endsWith('/')
|
||||
const pathStartsWithSlash = path.startsWith('/')
|
||||
|
||||
if (baseEndsWithSlash && pathStartsWithSlash) {
|
||||
// 都有斜杠,去掉 base 末尾那个
|
||||
return base.slice(0, -1) + path
|
||||
}
|
||||
else if (!baseEndsWithSlash && !pathStartsWithSlash) {
|
||||
// 都没有斜杠,补一个
|
||||
return `${base}/${path}`
|
||||
}
|
||||
else {
|
||||
// 只有一个斜杠,直接拼接
|
||||
return base + path
|
||||
}
|
||||
}
|
||||
|
||||
export function checkRequiredParams(options: Record<string, any>, keys: string[]): boolean {
|
||||
for (const key of keys) {
|
||||
const value = options[key]
|
||||
if (!value || value === 'undefined' || value === 'null') {
|
||||
toast.error(`参数 "${key}" 有误!`)
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1000)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* @Author: 菲鸽
|
||||
* @Date: 2024-03-28 19:13:55
|
||||
* @Last Modified by: 菲鸽
|
||||
* @Last Modified time: 2024-03-28 19:24:55
|
||||
*/
|
||||
export const platform = __UNI_PLATFORM__
|
||||
export const isH5 = __UNI_PLATFORM__ === 'h5'
|
||||
export const isApp = __UNI_PLATFORM__ === 'app'
|
||||
export const isMp = __UNI_PLATFORM__.startsWith('mp-')
|
||||
export const isMpWeixin = __UNI_PLATFORM__.startsWith('mp-weixin')
|
||||
export const isMpAplipay = __UNI_PLATFORM__.startsWith('mp-alipay')
|
||||
export const isMpToutiao = __UNI_PLATFORM__.startsWith('mp-toutiao')
|
||||
export const isHarmony = __UNI_PLATFORM__.startsWith('app-harmony')
|
||||
|
||||
const PLATFORM = {
|
||||
platform,
|
||||
isH5,
|
||||
isApp,
|
||||
isMp,
|
||||
isMpWeixin,
|
||||
isMpAplipay,
|
||||
isMpToutiao,
|
||||
isHarmony,
|
||||
}
|
||||
export default PLATFORM
|
||||
@@ -0,0 +1,28 @@
|
||||
export function getServerBaseUrl(): string {
|
||||
const origin = window.location.origin
|
||||
const publicBase = import.meta.env.VITE_APP_PUBLIC_BASE || '/'
|
||||
|
||||
// 拼接时去掉 publicBase 开头和结尾的斜杠(只保留中间路径)
|
||||
const cleanBase = publicBase.replace(/^\/+|\/+$/g, '')
|
||||
|
||||
return cleanBase ? `${origin}/${cleanBase}` : origin
|
||||
}
|
||||
|
||||
export async function getDbConfigs(): Promise<Record<string, any> | null> {
|
||||
const serverBaseUrl = getServerBaseUrl()
|
||||
|
||||
try {
|
||||
const response = await fetch(`${serverBaseUrl}/static/db.json`)
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch db.json:', response.status)
|
||||
return null
|
||||
}
|
||||
|
||||
const data: Record<string, any> = await response.json()
|
||||
return data
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error fetching db.json:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* toast 弹窗组件
|
||||
* 支持 success/error/warning/info 四种状态
|
||||
* 可配置 duration, position 等参数
|
||||
*/
|
||||
|
||||
type ToastType = 'success' | 'error' | 'warning' | 'info'
|
||||
|
||||
interface ToastOptions {
|
||||
type?: ToastType
|
||||
duration?: number
|
||||
position?: 'top' | 'middle' | 'bottom'
|
||||
icon?: 'success' | 'error' | 'none' | 'loading' | 'fail' | 'exception'
|
||||
message: string
|
||||
}
|
||||
|
||||
export function showToast(options: ToastOptions | string) {
|
||||
const defaultOptions: ToastOptions = {
|
||||
type: 'info',
|
||||
duration: 2000,
|
||||
position: 'middle',
|
||||
message: '',
|
||||
}
|
||||
const mergedOptions
|
||||
= typeof options === 'string'
|
||||
? { ...defaultOptions, message: options }
|
||||
: { ...defaultOptions, ...options }
|
||||
// 映射position到uniapp支持的格式
|
||||
const positionMap: Record<ToastOptions['position'], 'top' | 'bottom' | 'center'> = {
|
||||
top: 'top',
|
||||
middle: 'center',
|
||||
bottom: 'bottom',
|
||||
}
|
||||
|
||||
// 映射图标类型
|
||||
const iconMap: Record<
|
||||
ToastType,
|
||||
'success' | 'error' | 'none' | 'loading' | 'fail' | 'exception'
|
||||
> = {
|
||||
success: 'success',
|
||||
error: 'error',
|
||||
warning: 'fail',
|
||||
info: 'none',
|
||||
}
|
||||
|
||||
// 调用uni.showToast显示提示
|
||||
uni.showToast({
|
||||
title: mergedOptions.message,
|
||||
duration: mergedOptions.duration,
|
||||
position: positionMap[mergedOptions.position],
|
||||
icon: mergedOptions.icon || iconMap[mergedOptions.type],
|
||||
mask: true,
|
||||
})
|
||||
}
|
||||
|
||||
export const toast = {
|
||||
success: (message: string, options?: Omit<ToastOptions, 'type'>) =>
|
||||
showToast({ ...options, type: 'success', message }),
|
||||
error: (message: string, options?: Omit<ToastOptions, 'type'>) =>
|
||||
showToast({ ...options, type: 'error', message }),
|
||||
warning: (message: string, options?: Omit<ToastOptions, 'type'>) =>
|
||||
showToast({ ...options, type: 'warning', message }),
|
||||
info: (message: string, options?: Omit<ToastOptions, 'type'>) =>
|
||||
showToast({ ...options, type: 'info', message }),
|
||||
}
|
||||
Reference in new issue
Block a user