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: {
|
viewname: {
|
||||||
type: String,
|
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">
|
<script setup lang="ts">
|
||||||
|
import type { PropType } from 'vue'
|
||||||
import { useIndeterminate } from 'sard-uniapp'
|
import { useIndeterminate } from 'sard-uniapp'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
import { getAutocompleteDataApi, getAutocompleteDataExApi, getAutocompleteDataSingleApi } from '@/http/service'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -11,10 +13,6 @@ const props = defineProps({
|
|||||||
type: String as PropType<'radio' | 'multiple'>,
|
type: String as PropType<'radio' | 'multiple'>,
|
||||||
default: 'radio',
|
default: 'radio',
|
||||||
},
|
},
|
||||||
label: {
|
|
||||||
value: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
value: {
|
value: {
|
||||||
type: null as unknown as PropType<any | any[]>,
|
type: null as unknown as PropType<any | any[]>,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
@@ -30,8 +28,21 @@ const props = defineProps({
|
|||||||
viewname: {
|
viewname: {
|
||||||
type: String,
|
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({
|
const _visible = computed({
|
||||||
get: () => props.visible,
|
get: () => props.visible,
|
||||||
@@ -43,34 +54,35 @@ const _visible = computed({
|
|||||||
const _loading = ref(false)
|
const _loading = ref(false)
|
||||||
const searchValue = ref('')
|
const searchValue = ref('')
|
||||||
|
|
||||||
const _pagation = ref({
|
const pagination = ref({
|
||||||
current: 1,
|
current: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
total: 20,
|
total: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
const isPrevDisabled = computed(() => _pagation.value.current <= 1)
|
const isPrevDisabled = computed(() => pagination.value.current <= 1)
|
||||||
const isNextDisabled = computed(() =>
|
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
|
// 内部 value,双向绑定 props.value
|
||||||
const _value = computed({
|
const _value = computed({
|
||||||
get: () => {
|
get: () => {
|
||||||
// 如果是多选,保证返回数组
|
if (props.type === 'multiple') {
|
||||||
return props.type === 'multiple'
|
// 只在不是数组时才兜底成 []
|
||||||
? (Array.isArray(props.value) ? [...props.value] : [])
|
return Array.isArray(props.value) ? props.value : []
|
||||||
: props.value
|
}
|
||||||
|
return props.value
|
||||||
},
|
},
|
||||||
set: (val) => {
|
set: (val) => {
|
||||||
emit('update:value', val)
|
emit('update:value', val)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const _options = ref<any[]>([])
|
const options = ref<any[]>([])
|
||||||
|
|
||||||
function setOptions(val: any[]) {
|
function setOptions(val: any[]) {
|
||||||
_options.value = val.map(item => ({
|
options.value = val.map(item => ({
|
||||||
label: item[props.labelField] ?? '',
|
label: item[props.labelField] ?? '',
|
||||||
value: item[props.valueField] ?? undefined,
|
value: item[props.valueField] ?? undefined,
|
||||||
disabled: !!item.disabled,
|
disabled: !!item.disabled,
|
||||||
@@ -80,7 +92,7 @@ function setOptions(val: any[]) {
|
|||||||
|
|
||||||
const { allChecked, isIndeterminate, onAllChange } = useIndeterminate(
|
const { allChecked, isIndeterminate, onAllChange } = useIndeterminate(
|
||||||
_value,
|
_value,
|
||||||
_options,
|
options,
|
||||||
)
|
)
|
||||||
|
|
||||||
function select(option, toggle) {
|
function select(option, toggle) {
|
||||||
@@ -88,6 +100,14 @@ function select(option, toggle) {
|
|||||||
toggle(option.value)
|
toggle(option.value)
|
||||||
if (props.type === 'radio') {
|
if (props.type === 'radio') {
|
||||||
_visible.value = false
|
_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(() => {
|
const count = computed(() => {
|
||||||
if (props.type === 'multiple') {
|
if (props.type === 'multiple') {
|
||||||
return `${(_value.value).length || 0} / ${_options.value.length}`
|
return `${(_value.value).length || 0} / ${options.value.length}`
|
||||||
}
|
}
|
||||||
else {
|
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() {
|
function handleAfterEnter() {
|
||||||
// _loading.value = true
|
getData()
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePagationChange(page: number) {
|
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>
|
</script>
|
||||||
|
|
||||||
@@ -137,6 +233,7 @@ function handlePagationChange(page: number) {
|
|||||||
v-model="searchValue"
|
v-model="searchValue"
|
||||||
placeholder="请输入搜索的值"
|
placeholder="请输入搜索的值"
|
||||||
clearable
|
clearable
|
||||||
|
@confirm="handleSearch"
|
||||||
>
|
>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<sar-icon family="sari" name="search" />
|
<sar-icon family="sari" name="search" />
|
||||||
@@ -154,7 +251,7 @@ function handlePagationChange(page: number) {
|
|||||||
:throttle="false"
|
:throttle="false"
|
||||||
>
|
>
|
||||||
<sar-empty
|
<sar-empty
|
||||||
v-if="_options.length === 0"
|
v-if="options.length === 0"
|
||||||
:root-style="{ height: '100%' }"
|
:root-style="{ height: '100%' }"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -166,7 +263,7 @@ function handlePagationChange(page: number) {
|
|||||||
<template #custom="{ toggle }">
|
<template #custom="{ toggle }">
|
||||||
<sar-list inlaid>
|
<sar-list inlaid>
|
||||||
<sar-list-item
|
<sar-list-item
|
||||||
v-for="option in _options"
|
v-for="option in options"
|
||||||
:key="option.value"
|
:key="option.value"
|
||||||
:title="option.label"
|
:title="option.label"
|
||||||
@click="select(option, toggle)"
|
@click="select(option, toggle)"
|
||||||
@@ -191,7 +288,7 @@ function handlePagationChange(page: number) {
|
|||||||
<template #custom="{ toggle }">
|
<template #custom="{ toggle }">
|
||||||
<sar-list inlaid>
|
<sar-list inlaid>
|
||||||
<sar-list-item
|
<sar-list-item
|
||||||
v-for="option in _options"
|
v-for="option in options"
|
||||||
:key="option.value"
|
:key="option.value"
|
||||||
:title="option.label"
|
:title="option.label"
|
||||||
:hover="!option.disabled"
|
:hover="!option.disabled"
|
||||||
@@ -213,9 +310,9 @@ function handlePagationChange(page: number) {
|
|||||||
</view>
|
</view>
|
||||||
<view class="sar-popout__footer">
|
<view class="sar-popout__footer">
|
||||||
<sar-pagination
|
<sar-pagination
|
||||||
:current="_pagation.current"
|
:current="pagination.current"
|
||||||
:total="_pagation.total"
|
:total="pagination.total"
|
||||||
:page-size="_pagation.pageSize"
|
:page-size="pagination.pageSize"
|
||||||
type="simple"
|
type="simple"
|
||||||
root-class="picker-select__pagination-root"
|
root-class="picker-select__pagination-root"
|
||||||
@change="handlePagationChange"
|
@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 { createSSRApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import store from './store'
|
||||||
import '@/style/index.scss'
|
import '@/style/index.scss'
|
||||||
|
|
||||||
export function createApp() {
|
export function createApp() {
|
||||||
const app = createSSRApp(App)
|
const app = createSSRApp(App)
|
||||||
|
app.use(store)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
app,
|
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-list>
|
||||||
<sar-form label-width="240rpx">
|
<sar-form label-width="240rpx">
|
||||||
<sar-form-item label="Activity name">
|
<sar-form-item label="Activity name">
|
||||||
<picker-select-input />
|
<picker-select-input
|
||||||
</sar-form-item>
|
viewname="b_user"
|
||||||
<sar-form-item label="Activity name">
|
label-field="b_name"
|
||||||
<picker-select-input />
|
value-field="b_id"
|
||||||
|
search-type="ex"
|
||||||
|
/>
|
||||||
</sar-form-item>
|
</sar-form-item>
|
||||||
</sar-form>
|
</sar-form>
|
||||||
</template>
|
</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;
|
height: auto !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uni-toast {
|
||||||
|
z-index: 9999 !important; /* 根据实际情况进行调整 */
|
||||||
|
}
|
||||||
+4
-9
@@ -3,7 +3,7 @@
|
|||||||
declare global {
|
declare global {
|
||||||
interface IResData<T> {
|
interface IResData<T> {
|
||||||
code: number
|
code: number
|
||||||
msg: string
|
message: string
|
||||||
data: T
|
data: T
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,16 +17,11 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IUserInfo {
|
interface IUserInfo {
|
||||||
nickname?: string
|
orgid?: string // 机构码
|
||||||
avatar?: string
|
userid?: string // 主键id,登录账号
|
||||||
/** 微信的 openid,非微信没有这个字段 */
|
/** 微信的 openid,非微信没有这个字段 */
|
||||||
openid?: string
|
openid?: string
|
||||||
}
|
token?: string
|
||||||
|
|
||||||
interface IUserToken {
|
|
||||||
token: string
|
|
||||||
refreshToken?: string
|
|
||||||
refreshExpire?: number
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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