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 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,7 @@
1
+ import request from '../http.js'
2
+ // 获取菜单
3
+ export function menu() {
4
+ return request({
5
+ url: `/v3/menu/blocks?l2MenuId=&blockId=`,
6
+ })
7
+ }
@@ -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,10 @@
1
+ import { JSEncrypt } from 'jsencrypt'
2
+
3
+ // 加密
4
+ function rsaEncrypt(word: string, keyStr: string) {
5
+ const encrypt = new JSEncrypt()
6
+ encrypt.setPublicKey(keyStr)
7
+ return encrypt.encrypt(word)
8
+ }
9
+
10
+ export default rsaEncrypt
@@ -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>
@@ -0,0 +1,16 @@
1
+ <script setup lang="ts">
2
+ defineOptions({
3
+ name: 'WTest', // 定义组件名称
4
+ })
5
+ </script>
6
+
7
+ <template>
8
+ <view>
9
+ sadasd
10
+ <wd-button type="success">
11
+ 成功按钮
12
+ </wd-button>
13
+ </view>
14
+ </template>
15
+
16
+ <style scoped lang="scss"></style>