w-ui-v1 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Locale.ts +19 -0
- package/index.ts +26 -0
- package/package.json +20 -0
- package/utils/apis/login.ts +42 -0
- package/utils/apis/menu.ts +7 -0
- package/utils/apis/pageConfig.ts +35 -0
- package/utils/http.ts +54 -0
- package/utils/rsaEncrypt.ts +10 -0
- package/w-login/w-login.vue +117 -0
- package/w-menu/w-menu.vue +156 -0
- package/w-switch-lang/w-switch-lang.vue +39 -0
- package/w-table/components/foldCardTemplate.vue +243 -0
- package/w-table/w-table.vue +185 -0
- package/w-test/w-test.vue +16 -0
package/Locale.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Locale } from 'wot-design-uni'
|
|
2
|
+
// 引入英文语言包
|
|
3
|
+
import enUS from 'wot-design-uni/locale/lang/en-US'
|
|
4
|
+
// 引入中文语言包
|
|
5
|
+
import zhCN from 'wot-design-uni/locale/lang/zh-CN'
|
|
6
|
+
|
|
7
|
+
export function changeLanguage(lang: string = 'zh-CN') {
|
|
8
|
+
switch (lang) {
|
|
9
|
+
case 'en-US':
|
|
10
|
+
Locale.use('en-US', enUS)
|
|
11
|
+
break
|
|
12
|
+
case 'zh-CN':
|
|
13
|
+
Locale.use('zh-CN', zhCN)
|
|
14
|
+
break
|
|
15
|
+
default:
|
|
16
|
+
Locale.use('en-US', enUS)
|
|
17
|
+
break
|
|
18
|
+
}
|
|
19
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { App } from 'vue'
|
|
2
|
+
import wLogin from './w-login/w-login.vue'
|
|
3
|
+
import WMenu from './w-menu/w-menu.vue'
|
|
4
|
+
import wSwitchLang from './w-switch-lang/w-switch-lang.vue'
|
|
5
|
+
import WTable from './w-table/w-table.vue'
|
|
6
|
+
import wTest from './w-test/w-test.vue'
|
|
7
|
+
|
|
8
|
+
interface NamedComponent {
|
|
9
|
+
name: string
|
|
10
|
+
new (): any
|
|
11
|
+
}
|
|
12
|
+
const coms: NamedComponent[] = [
|
|
13
|
+
wTest,
|
|
14
|
+
wLogin,
|
|
15
|
+
wSwitchLang,
|
|
16
|
+
WMenu,
|
|
17
|
+
WTable,
|
|
18
|
+
]
|
|
19
|
+
// 批量组件注册
|
|
20
|
+
function install(Vue: App) {
|
|
21
|
+
coms.forEach((com) => {
|
|
22
|
+
Vue.component(com.name, com)
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default install // 这个方法以后再使用的时候可以被vue.use调用
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "w-ui-v1",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "w-ui",
|
|
5
|
+
"author": "wgxshh",
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
|
+
},
|
|
11
|
+
"peerDependencies": {
|
|
12
|
+
"wot-design-uni": "^1.7.0",
|
|
13
|
+
"z-paging": "^2.8.5"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"dayjs": "^1.11.13",
|
|
17
|
+
"jsencrypt": "^3.3.2",
|
|
18
|
+
"lodash": "^4.17.21"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import dayjs from 'dayjs'
|
|
2
|
+
import timezone from 'dayjs/plugin/timezone'
|
|
3
|
+
import utc from 'dayjs/plugin/utc'
|
|
4
|
+
import request from '../http.js'
|
|
5
|
+
import rsaEncrypt from '../rsaEncrypt.js'
|
|
6
|
+
|
|
7
|
+
// 获取公钥
|
|
8
|
+
export function pubKey() {
|
|
9
|
+
return request({
|
|
10
|
+
url: `/v3/auth/ras-pubkey`,
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 登录
|
|
15
|
+
export function login(username: string, password: string, pubkey: string, kaptchaToken: string = '', kaptchaText: string = '') {
|
|
16
|
+
dayjs.extend(utc)
|
|
17
|
+
dayjs.extend(timezone)
|
|
18
|
+
const datetime = dayjs(new Date())
|
|
19
|
+
.tz('Europe/London')
|
|
20
|
+
.format('YYYY-MM-DD HH:mm:ss')
|
|
21
|
+
const json = {
|
|
22
|
+
username,
|
|
23
|
+
password,
|
|
24
|
+
datetime,
|
|
25
|
+
}
|
|
26
|
+
const userInfo = rsaEncrypt(JSON.stringify(json), pubkey)
|
|
27
|
+
return request({
|
|
28
|
+
url: `/v3/auth/hctoken`,
|
|
29
|
+
data: {
|
|
30
|
+
userInfo,
|
|
31
|
+
kaptchaToken,
|
|
32
|
+
kaptchaText,
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
// 获取验证码
|
|
37
|
+
export function getKaptchaToken() {
|
|
38
|
+
return request({
|
|
39
|
+
url: `/v3/auth/kaptcha`,
|
|
40
|
+
method: 'POST',
|
|
41
|
+
})
|
|
42
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import request from '../http.js'
|
|
2
|
+
// 列表页面详细配置
|
|
3
|
+
export function pageConfig(sourceId: string) {
|
|
4
|
+
return request({
|
|
5
|
+
url: `/v3/ltmpl/config?sourceId=${sourceId}`,
|
|
6
|
+
})
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// 添加页面详细配置
|
|
10
|
+
export function addPageConfig(sourceId: string) {
|
|
11
|
+
return request({
|
|
12
|
+
url: `/v3/add-dtmpl/config?sourceId=${sourceId}`,
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 获取页面数据key
|
|
17
|
+
export function getPageKey(sourceId: string, query: string) {
|
|
18
|
+
return request({
|
|
19
|
+
url: `/v3/ltmpl/query/key?sourceId=${sourceId}&menuId=${sourceId}&${query}`,
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 获取页面数据
|
|
24
|
+
export function getPageData(queryKey: string, pageNo: number, pageSize: number) {
|
|
25
|
+
return request({
|
|
26
|
+
url: `/v3/ltmpl/query/data?queryKey=${queryKey}&pageNo=${pageNo}&pageSize=${pageSize}`,
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 获取当前页总条数
|
|
31
|
+
export function getPageTotal(queryKey: string) {
|
|
32
|
+
return request({
|
|
33
|
+
url: `/v3/ltmpl/query/count?queryKey=${queryKey}`,
|
|
34
|
+
})
|
|
35
|
+
}
|
package/utils/http.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// 用promise 封装uni.request
|
|
2
|
+
export default function request(options: any): Promise<any> {
|
|
3
|
+
const baseUrl = uni.getStorageSync('baseUrl') || '/'
|
|
4
|
+
const hydrocarbonProgramToken = uni.getStorageSync('hydrocarbonProgramToken') || ''
|
|
5
|
+
const token = uni.getStorageSync('token') || ''
|
|
6
|
+
const header = options.header || {}
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
uni.request({
|
|
9
|
+
url: baseUrl + options.url,
|
|
10
|
+
method: options.method || 'GET',
|
|
11
|
+
data: options.data || {},
|
|
12
|
+
header: {
|
|
13
|
+
'content-type': 'application/x-www-form-urlencoded',
|
|
14
|
+
'hydrocarbon-program-token': hydrocarbonProgramToken, // 项目code
|
|
15
|
+
'hydrocarbon-token': token, // 登录token
|
|
16
|
+
'hydrocarbon-client': 'MOBILE',
|
|
17
|
+
// #ifdef APP-PLUS
|
|
18
|
+
'hydrocarbon-app-version': '1.0.0',
|
|
19
|
+
// #endif
|
|
20
|
+
...header,
|
|
21
|
+
},
|
|
22
|
+
dataType: 'json',
|
|
23
|
+
// #ifndef MP-WEIXIN
|
|
24
|
+
responseType: 'json',
|
|
25
|
+
// #endif
|
|
26
|
+
success: (res: any) => {
|
|
27
|
+
// 判断状态
|
|
28
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
29
|
+
resolve(res)
|
|
30
|
+
}
|
|
31
|
+
if (res.statusCode === 401) {
|
|
32
|
+
uni.showToast({ title: '登录过期,请重新登录', icon: 'none' })
|
|
33
|
+
uni.removeStorageSync('token')
|
|
34
|
+
uni.reLaunch({ url: '/pages/login/login' })
|
|
35
|
+
// TODO 401
|
|
36
|
+
}
|
|
37
|
+
if (res.statusCode === 404) {
|
|
38
|
+
uni.showToast({ title: '请求失败', icon: 'none' })
|
|
39
|
+
reject(res)
|
|
40
|
+
// TODO 404
|
|
41
|
+
}
|
|
42
|
+
if (res.statusCode === 500) {
|
|
43
|
+
uni.showToast({ title: '服务器错误', icon: 'none' })
|
|
44
|
+
reject(res)
|
|
45
|
+
// TODO 500
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
fail: (err: any) => {
|
|
49
|
+
uni.showToast({ title: '请求失败', icon: 'none' })
|
|
50
|
+
reject(err)
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted, reactive, ref } from 'vue'
|
|
3
|
+
import { useToast } from 'wot-design-uni'
|
|
4
|
+
import { login, pubKey } from '../utils/apis/login'
|
|
5
|
+
import WSwitchLang from '../w-switch-lang/w-switch-lang.vue'
|
|
6
|
+
|
|
7
|
+
defineOptions({
|
|
8
|
+
name: 'WLogin',
|
|
9
|
+
})
|
|
10
|
+
// 定义props
|
|
11
|
+
const props = defineProps({
|
|
12
|
+
// 定义属性
|
|
13
|
+
loginImge: {
|
|
14
|
+
type: String,
|
|
15
|
+
default: '',
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
onMounted(() => {
|
|
19
|
+
getPublicKey()
|
|
20
|
+
})
|
|
21
|
+
const { success: showSuccess, error: showError, loading: showLoading } = useToast()
|
|
22
|
+
const pKey = ref('')
|
|
23
|
+
const model = reactive<{
|
|
24
|
+
username: string
|
|
25
|
+
password: string
|
|
26
|
+
}>({
|
|
27
|
+
username: '',
|
|
28
|
+
password: '',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const form = ref()
|
|
32
|
+
|
|
33
|
+
// 获取公匙
|
|
34
|
+
function getPublicKey() {
|
|
35
|
+
pubKey().then(({ data }: any) => {
|
|
36
|
+
pKey.value = data.rasPubkey
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 登录
|
|
41
|
+
function handleLogin() {
|
|
42
|
+
showLoading('登录中...')
|
|
43
|
+
login(model.username, model.password, pKey.value).then(({ data }: any) => {
|
|
44
|
+
if (data.status === 'error') {
|
|
45
|
+
return showError(data.message)
|
|
46
|
+
}
|
|
47
|
+
showSuccess('登录成功')
|
|
48
|
+
uni.setStorageSync('token', data.token)
|
|
49
|
+
uni.reLaunch({
|
|
50
|
+
url: '/pages/index/index',
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
function handleSubmit() {
|
|
55
|
+
form.value
|
|
56
|
+
.validate()
|
|
57
|
+
.then(({ valid }: any) => {
|
|
58
|
+
if (valid) {
|
|
59
|
+
handleLogin()
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
.catch((error: any) => {
|
|
63
|
+
console.error(error, 'error')
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<template>
|
|
69
|
+
<view class="login">
|
|
70
|
+
<WSwitchLang />
|
|
71
|
+
<view v-if="props.loginImge" class="avatar">
|
|
72
|
+
<wd-img width="200rpx" height="200rpx" :src="props.loginImge">
|
|
73
|
+
<template #error>
|
|
74
|
+
<view class="error-wrap">
|
|
75
|
+
<wd-icon name="image" size="200rpx" color="#ccc" />
|
|
76
|
+
</view>
|
|
77
|
+
</template>
|
|
78
|
+
</wd-img>
|
|
79
|
+
</view>
|
|
80
|
+
|
|
81
|
+
<wd-form ref="form" :model="model">
|
|
82
|
+
<wd-cell-group>
|
|
83
|
+
<wd-input
|
|
84
|
+
v-model="model.username" label="用户名" label-width="100px" prop="username" clearable
|
|
85
|
+
placeholder="请输入用户名" :rules="[{ required: true, message: '请填写用户名' }]"
|
|
86
|
+
/>
|
|
87
|
+
<wd-input
|
|
88
|
+
v-model="model.password" label="密码" label-width="100px" prop="password" show-password
|
|
89
|
+
clearable placeholder="请输入密码" :rules="[{ required: true, message: '请填写密码' }]"
|
|
90
|
+
/>
|
|
91
|
+
</wd-cell-group>
|
|
92
|
+
<view class="footer">
|
|
93
|
+
<wd-button type="primary" block @click="handleSubmit">
|
|
94
|
+
登录
|
|
95
|
+
</wd-button>
|
|
96
|
+
</view>
|
|
97
|
+
</wd-form>
|
|
98
|
+
<wd-toast />
|
|
99
|
+
</view>
|
|
100
|
+
</template>
|
|
101
|
+
|
|
102
|
+
<style scoped lang="scss">
|
|
103
|
+
.login {
|
|
104
|
+
.lang{
|
|
105
|
+
display: flex;
|
|
106
|
+
justify-content: flex-end;
|
|
107
|
+
}
|
|
108
|
+
.avatar {
|
|
109
|
+
display: flex;
|
|
110
|
+
justify-content: center;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.footer {
|
|
114
|
+
padding: 12px;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
</style>
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import _ from 'lodash'
|
|
3
|
+
import {
|
|
4
|
+
computed,
|
|
5
|
+
onMounted,
|
|
6
|
+
ref,
|
|
7
|
+
} from 'vue'
|
|
8
|
+
import { menu } from '../utils/apis/menu'
|
|
9
|
+
|
|
10
|
+
defineOptions({
|
|
11
|
+
name: 'WMenu',
|
|
12
|
+
})
|
|
13
|
+
const menuList = ref([])
|
|
14
|
+
const title = ref('')
|
|
15
|
+
const filtermenu = computed(() => {
|
|
16
|
+
return filterHiddenTree(menuList.value, 'items', true)
|
|
17
|
+
})
|
|
18
|
+
const sheetShow = ref(false)
|
|
19
|
+
const actions = ref([])
|
|
20
|
+
const loading = ref(false)
|
|
21
|
+
onMounted(() => {
|
|
22
|
+
getMenu()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
// 获取菜单数据
|
|
26
|
+
function getMenu() {
|
|
27
|
+
menu().then((res: any) => {
|
|
28
|
+
menuList.value = res.data.blocks
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
// 点击菜单跳转页面
|
|
32
|
+
function gotoPage(item: any) {
|
|
33
|
+
// 跳转页面
|
|
34
|
+
if (goto(item))
|
|
35
|
+
return
|
|
36
|
+
// 打开动作面板
|
|
37
|
+
openSheet(item)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 点击动作面板跳转页面
|
|
41
|
+
function sheetGotoPage(item: any) {
|
|
42
|
+
// 跳转页面
|
|
43
|
+
if (goto(item))
|
|
44
|
+
return
|
|
45
|
+
// 打开动作面板
|
|
46
|
+
openSheet(item, 300)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 跳转页面
|
|
50
|
+
function goto(item: any) {
|
|
51
|
+
if (item.pageType) {
|
|
52
|
+
uni.navigateTo({
|
|
53
|
+
url: `/pages/table/table?sourceId=${item.id}&pageType=${item.pageType}`,
|
|
54
|
+
success() {
|
|
55
|
+
// 通过eventChannel向被打开页面传送数据
|
|
56
|
+
// res.eventChannel.emit('acceptDataFromOpenerPage', {
|
|
57
|
+
// data: _.cloneDeep(item)
|
|
58
|
+
// })
|
|
59
|
+
},
|
|
60
|
+
fail(res: any) {
|
|
61
|
+
console.error(res, 'err')
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
return true
|
|
65
|
+
}
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 打开wd-action-sheet动作面板
|
|
70
|
+
function openSheet(item: any, s = 0) {
|
|
71
|
+
if (item.items) {
|
|
72
|
+
title.value = item.title
|
|
73
|
+
actions.value = item.items
|
|
74
|
+
? item.items.map((item: any) => {
|
|
75
|
+
return {
|
|
76
|
+
name: item.title,
|
|
77
|
+
// color: '#00000073',
|
|
78
|
+
..._.cloneDeep(item),
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
: []
|
|
82
|
+
|
|
83
|
+
// 设置二次打开的过渡时间
|
|
84
|
+
const id = setTimeout(() => {
|
|
85
|
+
sheetShow.value = true
|
|
86
|
+
clearTimeout(id)
|
|
87
|
+
}, s)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 根据 `disabled: true` 过滤树形数组(支持自定义子节点字段)
|
|
93
|
+
* @param {Array} tree 原始树形数据
|
|
94
|
+
* @param {string} [childKey] 子节点字段名
|
|
95
|
+
* @param {boolean} [deepClone] 是否深拷贝原数据
|
|
96
|
+
* @returns {Array} 过滤后的新树形数据
|
|
97
|
+
*/
|
|
98
|
+
function filterHiddenTree(tree: any, childKey = 'children', deepClone = false) {
|
|
99
|
+
// 深拷贝处理(可选)
|
|
100
|
+
const clone = (data: any) => {
|
|
101
|
+
if (!deepClone)
|
|
102
|
+
return data
|
|
103
|
+
return JSON.parse(JSON.stringify(data))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return clone(tree)
|
|
107
|
+
.filter((node: any) => !node.disabled) // 过滤当前层隐藏节点
|
|
108
|
+
.map((node: any) => {
|
|
109
|
+
// 递归处理子节点
|
|
110
|
+
if (Array.isArray(node[childKey])) {
|
|
111
|
+
node[childKey] = filterHiddenTree(node[childKey], childKey, deepClone)
|
|
112
|
+
}
|
|
113
|
+
return node
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<template>
|
|
119
|
+
<view>
|
|
120
|
+
<view class="img_box">
|
|
121
|
+
<!-- <wd-img height="250rpx" width="100%" src="../../static/bar.png" /> -->
|
|
122
|
+
</view>
|
|
123
|
+
<view v-if="loading" class="loading_box">
|
|
124
|
+
<wd-loading />
|
|
125
|
+
</view>
|
|
126
|
+
|
|
127
|
+
<view v-else-if="filtermenu.length > 0">
|
|
128
|
+
<wd-card v-for="(item, index) in filtermenu" :key="index" :title="item.title">
|
|
129
|
+
<template #default>
|
|
130
|
+
<wd-grid :column="5">
|
|
131
|
+
<wd-grid-item
|
|
132
|
+
v-for="(subItem, subIndex) in item.items" :key="subIndex" icon="picture"
|
|
133
|
+
:text="subItem.title" @click="gotoPage(subItem)"
|
|
134
|
+
/>
|
|
135
|
+
</wd-grid>
|
|
136
|
+
</template>
|
|
137
|
+
</wd-card>
|
|
138
|
+
</view>
|
|
139
|
+
<wd-status-tip v-else image="content" tip="暂无内容" />
|
|
140
|
+
<wd-action-sheet
|
|
141
|
+
v-model="sheetShow" :actions="actions" :title="title"
|
|
142
|
+
@select="({ item }) => { sheetGotoPage(item) }"
|
|
143
|
+
/>
|
|
144
|
+
</view>
|
|
145
|
+
</template>
|
|
146
|
+
|
|
147
|
+
<style lang="scss" scoped>
|
|
148
|
+
.img_box {
|
|
149
|
+
padding: 25rpx;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.loading_box {
|
|
153
|
+
display: flex;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
}
|
|
156
|
+
</style>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
// 修正导入路径
|
|
5
|
+
import { changeLanguage } from '../Locale'
|
|
6
|
+
|
|
7
|
+
defineOptions({ name: 'WSwitchLang' })
|
|
8
|
+
// 语言:zh-CN中文
|
|
9
|
+
const lang = ref('zh-CN')
|
|
10
|
+
const columns = ref([
|
|
11
|
+
{
|
|
12
|
+
label: '中文',
|
|
13
|
+
value: 'zh-CN',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
label: 'English',
|
|
17
|
+
value: 'en-US',
|
|
18
|
+
},
|
|
19
|
+
])
|
|
20
|
+
function confirm() {
|
|
21
|
+
// 修改语言
|
|
22
|
+
changeLanguage(lang.value)
|
|
23
|
+
}
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<view class="lang">
|
|
28
|
+
<view style="min-width: 300rpx;">
|
|
29
|
+
<wd-picker v-model="lang" label-width="40px" label="语言" size="mini" :columns="columns" @confirm="confirm" />
|
|
30
|
+
</view>
|
|
31
|
+
</view>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<style scoped lang="scss">
|
|
35
|
+
.lang{
|
|
36
|
+
display: flex;
|
|
37
|
+
justify-content: flex-end;
|
|
38
|
+
}
|
|
39
|
+
</style>
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
computed,
|
|
4
|
+
defineProps,
|
|
5
|
+
ref,
|
|
6
|
+
} from 'vue'
|
|
7
|
+
|
|
8
|
+
const props = defineProps({
|
|
9
|
+
page: {
|
|
10
|
+
type: Object,
|
|
11
|
+
required: true,
|
|
12
|
+
},
|
|
13
|
+
itemData: {
|
|
14
|
+
type: Object,
|
|
15
|
+
required: true,
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
// 处理单项数据、配置
|
|
20
|
+
const items = computed(() => {
|
|
21
|
+
return props.page.columns?.filter((item: any) => {
|
|
22
|
+
// 过滤掉操作列
|
|
23
|
+
return item.title !== '操作'
|
|
24
|
+
}).filter((item: any) => {
|
|
25
|
+
// 过滤掉title 中有 'y'字符的列
|
|
26
|
+
return !hasY(item.title)
|
|
27
|
+
}).map((item: any) => {
|
|
28
|
+
return {
|
|
29
|
+
title: item.title,
|
|
30
|
+
content: props.itemData?.fieldMap[item.id],
|
|
31
|
+
id: item.id,
|
|
32
|
+
mstrucId: item.mstrucId,
|
|
33
|
+
sourceId: item.sourceId,
|
|
34
|
+
}
|
|
35
|
+
}) || []
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// 单项数据转换
|
|
39
|
+
function getValue(content: any, title: any): any {
|
|
40
|
+
// 判断title 是否为字符串
|
|
41
|
+
if (typeof title === 'string') {
|
|
42
|
+
// 判断title 是否有 '时间'字符
|
|
43
|
+
if (title.includes('时间')) {
|
|
44
|
+
// 判断是否为数组
|
|
45
|
+
if (Array.isArray(content)) {
|
|
46
|
+
const newArr = content.map((item) => {
|
|
47
|
+
return formatTime(splitString(item))
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// 数组转换为字符串
|
|
51
|
+
return newArr.join(',')
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return formatTime(content)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 判断是否为数组
|
|
60
|
+
if (Array.isArray(content)) {
|
|
61
|
+
const newArr = content.map((item) => {
|
|
62
|
+
return splitString(item)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// 数组转换为字符串
|
|
66
|
+
return newArr.join(',')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 判断是否为JSON字符串
|
|
70
|
+
if (typeof content === 'string' && content.startsWith('{') && content.endsWith('}')) {
|
|
71
|
+
// 解析JSON字符串
|
|
72
|
+
const obj = JSON.parse(content)
|
|
73
|
+
// 图片链接
|
|
74
|
+
if (obj.base?.path && (obj.base?.type.includes('jpg') || obj.base?.type.includes('png'))) {
|
|
75
|
+
return {
|
|
76
|
+
url: `/files${obj.base.path}`,
|
|
77
|
+
type: '图片',
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 根据content中 "@R@" 分割字符串,取最后一项
|
|
83
|
+
return splitString(content)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 根据 "@R@" 分割字符串,取最后一项
|
|
87
|
+
function splitString(str: string) {
|
|
88
|
+
// 判断是否为字符串类型
|
|
89
|
+
if (typeof str !== 'string') {
|
|
90
|
+
// 如果不是字符串类型,直接返回
|
|
91
|
+
return str
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (str) {
|
|
95
|
+
// 判断是否有 "@R@"
|
|
96
|
+
if (str.includes('@R@')) {
|
|
97
|
+
// 分割字符串
|
|
98
|
+
const splitStr = str.split('@R@')
|
|
99
|
+
// 返回分割后的数组的最后一项
|
|
100
|
+
return splitStr[splitStr.length - 1]
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
// 返回原字符串
|
|
104
|
+
return str
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// 返回原字符串
|
|
108
|
+
return str
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 判断字符串中是否存在'y'字符
|
|
112
|
+
function hasY(str: string) {
|
|
113
|
+
// 判断是否为字符串
|
|
114
|
+
if (typeof str === 'string') {
|
|
115
|
+
return str.includes('y')
|
|
116
|
+
}
|
|
117
|
+
return false
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 格式化时间
|
|
121
|
+
function formatTime(str: string) {
|
|
122
|
+
if (typeof str === 'string') {
|
|
123
|
+
// 去掉小数点
|
|
124
|
+
return str.split('.')[0]
|
|
125
|
+
}
|
|
126
|
+
return str
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const isExpanded = ref(false)
|
|
130
|
+
const defaultVisible = 4 // 默认显示行数量
|
|
131
|
+
|
|
132
|
+
const visibleItems = computed(() => {
|
|
133
|
+
return isExpanded.value ? items.value : items.value.slice(0, defaultVisible)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
const showMore = computed(() => {
|
|
137
|
+
return items.value.length > defaultVisible
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
function toggleExpand() {
|
|
141
|
+
isExpanded.value = !isExpanded.value
|
|
142
|
+
}
|
|
143
|
+
</script>
|
|
144
|
+
|
|
145
|
+
<template>
|
|
146
|
+
<wd-card :title="getValue(visibleItems[0]?.content, visibleItems[0]?.title) || ' '">
|
|
147
|
+
<view class="table_collapse">
|
|
148
|
+
<view v-for="(item, index) in visibleItems" :key="index" class="cloum">
|
|
149
|
+
<wd-row style="border-bottom:1px solid #e8e5e5;padding: 10rpx;">
|
|
150
|
+
<wd-col :span="6">
|
|
151
|
+
<view class="lable">
|
|
152
|
+
<text>{{ item.title }}:</text>
|
|
153
|
+
</view>
|
|
154
|
+
</wd-col>
|
|
155
|
+
<wd-col :span="18">
|
|
156
|
+
<wd-img
|
|
157
|
+
v-if="getValue(item.content, item.title)?.type === '图片'" width="100rpx" height="100rpx"
|
|
158
|
+
:src="getValue(item.content, item.title).url" :enable-preview="true"
|
|
159
|
+
/>
|
|
160
|
+
<view v-else class="value">
|
|
161
|
+
{{ getValue(item.content, item.title) }}
|
|
162
|
+
</view>
|
|
163
|
+
</wd-col>
|
|
164
|
+
</wd-row>
|
|
165
|
+
</view>
|
|
166
|
+
<wd-button
|
|
167
|
+
v-if="showMore" type="icon" :icon="isExpanded ? 'arrow-up' : 'arrow-down'" size="small"
|
|
168
|
+
custom-class="expand-btn" @click="toggleExpand"
|
|
169
|
+
/>
|
|
170
|
+
</view>
|
|
171
|
+
<template #footer>
|
|
172
|
+
<wd-button size="small" class="btn">
|
|
173
|
+
删除
|
|
174
|
+
</wd-button>
|
|
175
|
+
<wd-button size="small" class="btn">
|
|
176
|
+
编辑
|
|
177
|
+
</wd-button>
|
|
178
|
+
</template>
|
|
179
|
+
</wd-card>
|
|
180
|
+
</template>
|
|
181
|
+
|
|
182
|
+
<style lang="scss" scoped>
|
|
183
|
+
.table_collapse {
|
|
184
|
+
|
|
185
|
+
color: #000;
|
|
186
|
+
background-color: #F8F8F8;
|
|
187
|
+
padding: 20rpx;
|
|
188
|
+
|
|
189
|
+
/* 添加过渡动画 */
|
|
190
|
+
.cloum {
|
|
191
|
+
transition: all 0.3s ease;
|
|
192
|
+
overflow: hidden;
|
|
193
|
+
max-height: 100px;
|
|
194
|
+
color: #3e3e3e;
|
|
195
|
+
|
|
196
|
+
.lable {
|
|
197
|
+
// font-weight: 600;
|
|
198
|
+
text-align: left;
|
|
199
|
+
margin-right: 20rpx;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.value {
|
|
203
|
+
color: #000;
|
|
204
|
+
// text-align: justify; /* 文字两端对齐 */
|
|
205
|
+
// word-break: break-all; /* 强制英文/数字换行时断词 */
|
|
206
|
+
word-break: break-all;
|
|
207
|
+
/* 所有字符(包括中文)强制换行 */
|
|
208
|
+
white-space: normal;
|
|
209
|
+
/* 确保不保留空白符 */
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
position: relative; // 添加定位上下文
|
|
215
|
+
padding-bottom: 80rpx; // 为按钮预留空间
|
|
216
|
+
|
|
217
|
+
.expand-btn {
|
|
218
|
+
/* 尺寸调整 */
|
|
219
|
+
padding: 8rpx 20rpx;
|
|
220
|
+
font-size: 24rpx;
|
|
221
|
+
line-height: 1.2;
|
|
222
|
+
|
|
223
|
+
/* 定位调整 */
|
|
224
|
+
position: absolute;
|
|
225
|
+
right: 50%;
|
|
226
|
+
transform: translateX(50%);
|
|
227
|
+
bottom: 0;
|
|
228
|
+
width: auto !important;
|
|
229
|
+
margin-top: 0;
|
|
230
|
+
|
|
231
|
+
/* 视觉样式 */
|
|
232
|
+
// background: #ffffff;
|
|
233
|
+
// border: 1px solid #ebedf0;
|
|
234
|
+
// border-radius: 16rpx;
|
|
235
|
+
// box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.btn {
|
|
241
|
+
margin-left: 10rpx;
|
|
242
|
+
}
|
|
243
|
+
</style>
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onLoad } from '@dcloudio/uni-app'
|
|
3
|
+
import {
|
|
4
|
+
computed,
|
|
5
|
+
ref,
|
|
6
|
+
} from 'vue'
|
|
7
|
+
import {
|
|
8
|
+
getPageData,
|
|
9
|
+
getPageKey,
|
|
10
|
+
pageConfig,
|
|
11
|
+
} from '../utils/apis/pageConfig'
|
|
12
|
+
import foldCardTemplate from './components/foldCardTemplate.vue'
|
|
13
|
+
|
|
14
|
+
defineOptions({
|
|
15
|
+
name: 'WTable',
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
// 搜索类型
|
|
19
|
+
const searchType = ref('全部')
|
|
20
|
+
// 搜索参数
|
|
21
|
+
const searchParams = ref('')
|
|
22
|
+
const searchVal = ref('')
|
|
23
|
+
const sourceId = ref('')
|
|
24
|
+
const pageData = ref<{
|
|
25
|
+
criterias: []
|
|
26
|
+
buttons: string[]
|
|
27
|
+
}>({ criterias: [], buttons: [] })
|
|
28
|
+
onLoad((option: any) => {
|
|
29
|
+
sourceId.value = option.sourceId || '405889315156828169'
|
|
30
|
+
getPageConfig()
|
|
31
|
+
})
|
|
32
|
+
// 获取页面配置
|
|
33
|
+
function getPageConfig() {
|
|
34
|
+
pageConfig(sourceId.value).then((res: any) => {
|
|
35
|
+
pageData.value = res.data.ltmplConfig
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
// 搜索菜单数据
|
|
39
|
+
const menu = computed(() => {
|
|
40
|
+
const menulist = pageData.value.criterias?.map((item: any) => {
|
|
41
|
+
return {
|
|
42
|
+
content: item.title,
|
|
43
|
+
id: item.id,
|
|
44
|
+
mstrucId: item.mstrucId,
|
|
45
|
+
sourceId: item.sourceId,
|
|
46
|
+
}
|
|
47
|
+
}) || []
|
|
48
|
+
menulist.unshift({
|
|
49
|
+
content: '全部',
|
|
50
|
+
id: '',
|
|
51
|
+
mstrucId: '',
|
|
52
|
+
sourceId: '',
|
|
53
|
+
})
|
|
54
|
+
return menulist
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// 搜索类型切换
|
|
58
|
+
function changeSearchType({ item }: any) {
|
|
59
|
+
searchType.value = item.content
|
|
60
|
+
searchParams.value = `c_${item.id}=`
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// z-paging实例
|
|
64
|
+
const paging = ref()
|
|
65
|
+
// 列表数据 v-model绑定的这个变量不要在分页请求结束中自己赋值,直接使用即可
|
|
66
|
+
const dataList = ref([])
|
|
67
|
+
|
|
68
|
+
// 点击搜索按钮
|
|
69
|
+
function search() {
|
|
70
|
+
paging.value.reload() // 手动触发列表重新加载
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// @query所绑定的方法不要自己调用!!需要刷新列表数据时,只需要调用paging.value.reload()即可
|
|
74
|
+
async function queryList(pageNo: number, pageSize: number) {
|
|
75
|
+
// 此处请求仅为演示,请替换为自己项目中的请求
|
|
76
|
+
// request.queryList({ pageNo,pageSize }).then(res => {
|
|
77
|
+
// 将请求结果通过complete传给z-paging处理,同时也代表请求结束,这一行必须调用
|
|
78
|
+
// paging.value.complete(res.data.list);
|
|
79
|
+
// }).catch(res => {
|
|
80
|
+
// 如果请求失败写paging.value.complete(false);
|
|
81
|
+
// 注意,每次都需要在catch中写这句话很麻烦,z-paging提供了方案可以全局统一处理
|
|
82
|
+
// 在底层的网络请求抛出异常时,写uni.$emit('z-paging-error-emit');即可
|
|
83
|
+
// paging.value.complete(false);
|
|
84
|
+
// paging.value.reload() // 手动触发加载
|
|
85
|
+
// })
|
|
86
|
+
try {
|
|
87
|
+
let searchQuery = searchParams.value + searchVal.value
|
|
88
|
+
if (searchType.value === '全部') {
|
|
89
|
+
searchParams.value = ''
|
|
90
|
+
searchVal.value = ''
|
|
91
|
+
searchQuery = ''
|
|
92
|
+
}
|
|
93
|
+
const key = await getPageKey(sourceId.value, searchQuery)// 获取key
|
|
94
|
+
const data = await getPageData(key.data?.key, pageNo, pageSize)// 获取数据
|
|
95
|
+
// const count = await getPageTotal(key.data?.key)// 获取总数
|
|
96
|
+
paging.value.complete(data.data.entities)
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
paging.value.complete(false)
|
|
100
|
+
console.error(error)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 点击添加按钮
|
|
105
|
+
function add() {
|
|
106
|
+
uni.navigateTo({
|
|
107
|
+
url: `/pages/dynamic/dynamic?sourceId=${sourceId.value}&pageType=新增列表数据&pageTitle=`,
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
</script>
|
|
111
|
+
|
|
112
|
+
<template>
|
|
113
|
+
<z-paging ref="paging" v-model="dataList" :show-loading-more-when-reload="true" @query="queryList">
|
|
114
|
+
<template #top>
|
|
115
|
+
<view style="margin-bottom: 20rpx;">
|
|
116
|
+
<!-- 搜索框 -->
|
|
117
|
+
<wd-search v-model="searchVal" cancel-txt="搜索" @cancel="search">
|
|
118
|
+
<template #prefix>
|
|
119
|
+
<wd-popover mode="menu" :content="menu" @menuclick="changeSearchType">
|
|
120
|
+
<view class="search-type">
|
|
121
|
+
<text class="searchType">
|
|
122
|
+
{{ searchType }}
|
|
123
|
+
</text>
|
|
124
|
+
<wd-icon custom-class="icon-arrow" name="fill-arrow-down" />
|
|
125
|
+
</view>
|
|
126
|
+
</wd-popover>
|
|
127
|
+
</template>
|
|
128
|
+
</wd-search>
|
|
129
|
+
<view class="buttons-box">
|
|
130
|
+
<!-- 添加按钮 -->
|
|
131
|
+
<wd-button v-if="pageData.buttons.includes('dtmplAdd')" type="icon" icon="add" @click="add" />
|
|
132
|
+
</view>
|
|
133
|
+
</view>
|
|
134
|
+
</template>
|
|
135
|
+
<!-- 卡片 -->
|
|
136
|
+
<foldCardTemplate v-for="(item, index) in dataList" :key="index" :page="pageData" :item-data="item" />
|
|
137
|
+
</z-paging>
|
|
138
|
+
</template>
|
|
139
|
+
|
|
140
|
+
<style scoped lang="scss">
|
|
141
|
+
.search-type {
|
|
142
|
+
position: relative;
|
|
143
|
+
height: 30px;
|
|
144
|
+
line-height: 30px;
|
|
145
|
+
padding: 0 8px 0 16px;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.search-type::after {
|
|
149
|
+
position: absolute;
|
|
150
|
+
content: '';
|
|
151
|
+
width: 1px;
|
|
152
|
+
right: 0;
|
|
153
|
+
top: 5px;
|
|
154
|
+
bottom: 5px;
|
|
155
|
+
background: rgba(0, 0, 0, 0.25);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.search-type {
|
|
159
|
+
:deep(.icon-arrow) {
|
|
160
|
+
display: inline-block;
|
|
161
|
+
font-size: 40rpx;
|
|
162
|
+
vertical-align: middle;
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.searchType {
|
|
168
|
+
font-size: 30rpx;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.card_box {
|
|
172
|
+
margin-top: 20rpx;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.loading_box {
|
|
176
|
+
display: flex;
|
|
177
|
+
justify-content: center;
|
|
178
|
+
margin-top: 40rpx;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.buttons-box {
|
|
182
|
+
text-align: right;
|
|
183
|
+
background-color: #fff;
|
|
184
|
+
}
|
|
185
|
+
</style>
|