wui-components-v2 1.0.98 → 1.1.1

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.
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, defineEmits, defineOptions, defineProps, ref } from 'vue'
3
- import type { Groups } from '../../type'
3
+ import type { Enums, Groups } from '../../type'
4
4
  import formControl from '../form-control/form-control.vue'
5
5
  import { actionDataSave } from '../../api/page'
6
6
  import { useGlobalToast } from '../../composables/useGlobalToast'
@@ -14,6 +14,7 @@ const props = defineProps<
14
14
  show: boolean
15
15
  code: string
16
16
  zpaging: any
17
+ enumColumn?: Enums
17
18
  }
18
19
  >()
19
20
  const emits = defineEmits(['update:show'])
@@ -58,7 +59,7 @@ async function action() {
58
59
  <view class="h-50vh w-100vw">
59
60
  <view class="h-44px .dark:bg-gray-900 .light:bg-white" />
60
61
  <view class="w-[calc(100%-44px)] overflow-auto .dark:bg-gray-900 .light:bg-white">
61
- <formControl v-if="props.fieldGroup" ref="formControlRef" :field-group="props.fieldGroup" />
62
+ <formControl v-if="props.fieldGroup" ref="formControlRef" :field-group="props.fieldGroup" :enum-column="enumColumn" />
62
63
  <view class="h-44px .dark:bg-gray-900 .light:bg-white" />
63
64
  <view class="fixed bottom-0 left-0 right-0 box-border w-100% flex p-2 .dark:bg-gray-900 .light:bg-white">
64
65
  <wd-button :loading="btnLoading" custom-class="flex-1" @click="() => { action() }">
@@ -0,0 +1,220 @@
1
+ <script setup>
2
+ import { computed, defineEmits, defineExpose, defineOptions, defineProps, ref, watch } from 'vue'
3
+ import { useManualTheme } from '../../composables/useManualTheme'
4
+
5
+ defineOptions({
6
+ name: 'AudioPlay',
7
+ })
8
+ const props = defineProps({
9
+ src: {
10
+ type: String,
11
+ required: true,
12
+ },
13
+ autoplay: {
14
+ type: Boolean,
15
+ default: false,
16
+ },
17
+ })
18
+ const emit = defineEmits(['play', 'pause'])
19
+ const { primary } = useManualTheme()
20
+ const innerAudioContext = ref(null)
21
+ const isPlaying = ref(false)
22
+ const currentTime = ref(0)
23
+ const duration = ref(0)
24
+
25
+ // 计算进度百分比
26
+ const progressPercent = computed(() => {
27
+ return duration.value > 0 ? (currentTime.value / duration.value) * 100 : 0
28
+ })
29
+
30
+ // 格式化时间显示
31
+ function formatTime(seconds) {
32
+ if (Number.isNaN(seconds))
33
+ return '00:00'
34
+ const mins = Math.floor(seconds / 60)
35
+ const secs = Math.floor(seconds % 60)
36
+ return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
37
+ }
38
+
39
+ // 初始化音频
40
+ function initAudio() {
41
+ innerAudioContext.value = uni.createInnerAudioContext()
42
+ innerAudioContext.value.src = props.src
43
+ innerAudioContext.value.autoplay = props.autoplay
44
+
45
+ innerAudioContext.value.onPlay(() => {
46
+ isPlaying.value = true
47
+ emit('play')
48
+ })
49
+
50
+ innerAudioContext.value.onPause(() => {
51
+ isPlaying.value = false
52
+ emit('pause')
53
+ })
54
+
55
+ innerAudioContext.value.onTimeUpdate(() => {
56
+ currentTime.value = innerAudioContext.value.currentTime
57
+ duration.value = innerAudioContext.value.duration
58
+ })
59
+
60
+ innerAudioContext.value.onError((err) => {
61
+ isPlaying.value = false
62
+ console.error('播放错误:', err)
63
+ })
64
+
65
+ innerAudioContext.value.onEnded(() => {
66
+ isPlaying.value = false // 更新状态
67
+ currentTime.value = 0 // 重置进度
68
+ // emit('ended'); // 通知父组件
69
+ })
70
+ }
71
+
72
+ // 播放控制
73
+ function play() {
74
+ if (!innerAudioContext.value)
75
+ initAudio()
76
+ innerAudioContext.value.play()
77
+ }
78
+
79
+ function pause() {
80
+ if (innerAudioContext.value) {
81
+ innerAudioContext.value.pause()
82
+ }
83
+ }
84
+
85
+ function togglePlay() {
86
+ if (isPlaying.value) {
87
+ pause()
88
+ }
89
+ else {
90
+ play()
91
+ }
92
+ }
93
+
94
+ // 监听src变化
95
+ watch(() => props.src, (newVal) => {
96
+ if (innerAudioContext.value) {
97
+ innerAudioContext.value.src = newVal
98
+ }
99
+ else {
100
+ initAudio()
101
+ }
102
+ })
103
+
104
+ // 暴露方法
105
+ defineExpose({
106
+ play,
107
+ pause,
108
+ isPlaying,
109
+ })
110
+
111
+ // 初始化
112
+ initAudio()
113
+ </script>
114
+
115
+ <template>
116
+ <view class="mini-audio-player">
117
+ <!-- 播放/暂停按钮 -->
118
+ <button class="mini-play-btn" :style="{ background: primary }" @click="togglePlay">
119
+ <wd-icon v-if="isPlaying" name="pause" size="22px" color="#fff" />
120
+ <wd-icon v-else name="play" size="22px" color="#fff" />
121
+ </button>
122
+
123
+ <!-- 时间显示 -->
124
+ <text class="time-display">
125
+ {{ formatTime(currentTime) }}
126
+ </text>
127
+
128
+ <!-- 迷你进度条 -->
129
+ <view class="mini-progress">
130
+ <view class="mini-progress-bar" :style="{ width: `${progressPercent}%`, background: primary }" />
131
+ </view>
132
+
133
+ <!-- 总时间 -->
134
+ <text class="time-display">
135
+ {{ formatTime(duration) }}
136
+ </text>
137
+ </view>
138
+ </template>
139
+
140
+ <style scoped>
141
+ .mini-audio-player {
142
+ display: flex;
143
+ align-items: center;
144
+ height: 36px;
145
+ background-color: #f5f5f5;
146
+ border-radius: 18px;
147
+ padding: 0 5px;
148
+ width: 180px;
149
+ }
150
+
151
+ .mini-play-btn {
152
+ width: 28px;
153
+ height: 28px;
154
+ border-radius: 50%;
155
+ background: #1890ff;
156
+ border: none;
157
+ padding: 0;
158
+ display: flex;
159
+ align-items: center;
160
+ justify-content: center;
161
+ margin-right: 8px;
162
+ }
163
+
164
+ .mini-icon {
165
+ width: 12px;
166
+ height: 12px;
167
+ position: relative;
168
+ margin-left: 1px;
169
+ }
170
+
171
+ .mini-icon::before {
172
+ content: '';
173
+ position: absolute;
174
+ left: 0;
175
+ top: 0;
176
+ border-top: 6px solid transparent;
177
+ border-bottom: 6px solid transparent;
178
+ border-left: 10px solid white;
179
+ }
180
+
181
+ .mini-icon.playing::before,
182
+ .mini-icon.playing::after {
183
+ content: '';
184
+ position: absolute;
185
+ width: 3px;
186
+ height: 12px;
187
+ background: white;
188
+ }
189
+
190
+ .mini-icon.playing::before {
191
+ left: 3px;
192
+ }
193
+
194
+ .mini-icon.playing::after {
195
+ left: 8px;
196
+ }
197
+
198
+ .mini-progress {
199
+ flex: 1;
200
+ height: 3px;
201
+ background: #d9d9d9;
202
+ border-radius: 2px;
203
+ overflow: hidden;
204
+ margin: 0 8px;
205
+ }
206
+
207
+ .mini-progress-bar {
208
+ height: 100%;
209
+ background: #1890ff;
210
+ transition: width 0.3s ease;
211
+ }
212
+
213
+ .time-display {
214
+ font-size: 10px;
215
+ color: #666;
216
+ min-width: 32px;
217
+ text-align: center;
218
+ font-family: monospace;
219
+ }
220
+ </style>
@@ -2,7 +2,7 @@
2
2
  import { computed, defineOptions, defineProps, ref } from 'vue'
3
3
  import { useRouter } from 'uni-mini-router'
4
4
  import { onLoad } from '@dcloudio/uni-app'
5
- import type { ClassEditConfigs, Entities, Fields, Groups, rowActions } from '../../type'
5
+ import type { ClassEditConfigs, Entities, Enums, Fields, Groups, rowActions } from '../../type'
6
6
  import ActionPopup from '../action-popup/action-popup.vue'
7
7
  import { actionDataSave } from '../../api/page'
8
8
  import { useGlobalToast } from '../../composables/useGlobalToast'
@@ -20,6 +20,8 @@ const props = defineProps<{
20
20
  rowActions?: rowActions[]
21
21
  ractions?: rowActions[]
22
22
  zpaging?: any
23
+ enumColumn?: Enums
24
+ detailButtonHandle?: () => void
23
25
  }>()
24
26
  const toast = useGlobalToast()
25
27
  const pageType = ref('')
@@ -86,6 +88,10 @@ function raction(subitem: rowActions) {
86
88
 
87
89
  // 跳转详情页面
88
90
  function detail() {
91
+ if (props.detailButtonHandle) {
92
+ props.detailButtonHandle()
93
+ return
94
+ }
89
95
  router.push(`/pages/details-page/index?sourceId=${props.sourceId}&id=${props.code}&title=${props.item.title}`)
90
96
  }
91
97
 
@@ -120,6 +126,6 @@ function isShowAction(item: rowActions) {
120
126
  <wd-button v-if="props.item.buttons.includes('detail')" size="small" type="info" @click="detail()">
121
127
  详情
122
128
  </wd-button>
123
- <ActionPopup v-model:show="actionItemShow" :zpaging="props.zpaging" :field-group="actionItem" :code="props.code" />
129
+ <ActionPopup v-model:show="actionItemShow" :enum-column="enumColumn" :zpaging="props.zpaging" :field-group="actionItem" :code="props.code" />
124
130
  </view>
125
131
  </template>
@@ -19,6 +19,7 @@ const props = defineProps<{
19
19
  sourceId: string
20
20
  groups: Groups
21
21
  enumColumn?: Enums
22
+ index: number
22
23
  }>()
23
24
  const contentRef = ref<any>()
24
25
  // 展示内容
@@ -106,12 +107,12 @@ function toggleCollapse(contentId: string) {
106
107
  <view class="mb-3">
107
108
  <!-- 订单信息 -->
108
109
  <view class="text-sm space-y-3">
109
- <LabelValue :exhibit-data="exhibitData" :data="data" />
110
+ <LabelValue :exhibit-data="exhibitData" :data="data" :index="index" />
110
111
 
111
112
  <!-- 可折叠内容 -->
112
113
  <view :id="`${data.code}`" ref="contentRef">
113
114
  <view class="pt-3 space-y-3">
114
- <LabelValue :exhibit-data="collapseData" :data="data" />
115
+ <LabelValue :exhibit-data="collapseData" :data="data" :index="index" />
115
116
  </view>
116
117
  </view>
117
118
  </view>
@@ -119,6 +120,7 @@ function toggleCollapse(contentId: string) {
119
120
 
120
121
  <!-- 订单底部 -->
121
122
  <view class="flex items-center justify-between border-t border-gray-100">
123
+ <!-- 折叠按钮 -->
122
124
  <view>
123
125
  <wd-button
124
126
  v-if="showCollapse"
@@ -4,6 +4,7 @@ import type { Columns, Entities } from '../../type'
4
4
  import { downloadFile, formatItemData } from '../../utils'
5
5
  import ControlTypeSupportor from '../../utils/control-type-supportor'
6
6
  import VideoPlay from '../video-play/video-play.vue'
7
+ import AudioPlay from '../audio-play/audio-play.vue'
7
8
  import { useManualTheme } from '../../composables/useManualTheme'
8
9
 
9
10
  defineOptions({
@@ -12,11 +13,13 @@ defineOptions({
12
13
  const props = defineProps<{
13
14
  exhibitData: Columns[]
14
15
  data: Entities
16
+ index: number
15
17
  }>()
16
18
  const { primary } = useManualTheme()
17
19
  const clums = computed(() => {
18
20
  return props.exhibitData.filter(item => !item.title?.includes('y'))
19
21
  })
22
+
20
23
  const videoPlayRef = ref()
21
24
  function openVideo(uitem: any) {
22
25
  videoPlayRef.value.open(uitem.url)
@@ -28,12 +31,12 @@ function isControlType(item: Columns): string {
28
31
  </script>
29
32
 
30
33
  <template>
31
- <view v-for="(item, index) in clums" :key="index" class="flex">
34
+ <view v-for="(item, sindex) in clums" :key="sindex" class="flex">
32
35
  <view class="mr-2 w-20 p-1 text-gray-500 dark:text-white">
33
36
  {{ item.title }}:
34
37
  </view>
35
38
  <view
36
- v-if="isControlType(item) === 'file' || isControlType(item) === 'relfile'"
39
+ v-if="isControlType(item) === 'file' || isControlType(item) === 'relfile' || isControlType(item) === 'video'"
37
40
  class="flex flex-1 items-center text-gray-800 dark:text-white"
38
41
  >
39
42
  <view v-if="data.fieldMap[item.sourceId]">
@@ -63,6 +66,13 @@ function isControlType(item: Columns): string {
63
66
  >
64
67
  <wd-icon name="play-circle-filled" size="22px" />
65
68
  </view>
69
+ <!-- 音频 -->
70
+ <view
71
+ v-else-if="['mp3', 'wma', 'wav', 'aac', 'ogg', 'flac', 'm4a', 'amr'].includes(uitem.type)"
72
+ style="display: flex;align-items: center;justify-content: center;"
73
+ >
74
+ <AudioPlay :src="uitem.url" />
75
+ </view>
66
76
  <!-- 文件 -->
67
77
  <view v-else-if="['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'txt', 'zip', 'rar', '7z', 'gz', 'bz2', 'tar', 'iso', 'exe', 'dmg', 'apk', 'apk', 'apk', 'apk', 'apk', 'apk', 'apk', 'apk'].includes(uitem.type)">
68
78
  <span :style="{ color: primary }" @click="downloadFile(uitem.url)">
@@ -72,6 +82,9 @@ function isControlType(item: Columns): string {
72
82
  </view>
73
83
  </view>
74
84
  </view>
85
+ <view v-else-if="item.title === '序号'" class="flex flex-1 items-center text-gray-800 dark:text-white">
86
+ {{ index + 1 }}
87
+ </view>
75
88
  <view v-else class="flex flex-1 items-center text-gray-800 dark:text-white">
76
89
  {{ formatItemData(data.fieldMap[item.sourceId], isControlType(item)) }}
77
90
  </view>
@@ -125,13 +125,13 @@ async function getDetailData() {
125
125
  </view>
126
126
  <view v-else>
127
127
  <wd-collapse v-model="collapses">
128
- <wd-collapse-item v-for="group in pageConfig.groups" :key="group.id" :title="group.title" :name="group.id">
129
- <view v-if="group.type === 'fieldGroup'">
130
- <LabelValue :exhibit-data="group.fields" :data="data" />
128
+ <wd-collapse-item v-for="(group, index) in pageConfig.groups" :key="group.id" :title="group.title" :name="group.id">
129
+ <view v-if="group.type === 'fieldGroup'" class="text-sm space-y-3">
130
+ <LabelValue :index="index" :exhibit-data="group.fields" :data="data" />
131
131
  </view>
132
132
  <view v-if="group.type === 'relation'">
133
- <view v-for="field in data.arrayMap[group.id]" :key="field.code">
134
- <foldCard :enum-column="{}" :groups="group" :source-id="sourceId" :columns="group.fields" model="complex" :data="field">
133
+ <view v-for="(field, subindex) in data.arrayMap[group.id]" :key="field.code">
134
+ <foldCard :index="subindex" :enum-column="{}" :groups="group" :source-id="sourceId" :columns="group.fields" model="complex" :data="field">
135
135
  <template #buttons>
136
136
  <CardBotomButtons :source-id="group.id" :item="groups" :code="field.code" :data="data" />
137
137
  </template>
@@ -699,8 +699,8 @@ function handleFileChange(e: any, group: Groups) {
699
699
  新增
700
700
  </wd-button>
701
701
  </view>
702
- <view v-for="field in pageConfig?.entity?.arrayMap[group.id]" :key="field.code">
703
- <foldCard :enum-column="{}" :groups="group" :source-id="sourceId" :columns="group.fields" model="complex" :data="field">
702
+ <view v-for="(field, index) in pageConfig?.entity?.arrayMap[group.id]" :key="field.code">
703
+ <foldCard :index="index" :enum-column="{}" :groups="group" :source-id="sourceId" :columns="group.fields" model="complex" :data="field">
704
704
  <template v-if="!group.readOnly" #buttons>
705
705
  <wd-button v-if="group.buttons.includes('dtmplEdit')" size="small" @click="edit(group, field)">
706
706
  编辑
@@ -1,5 +1,5 @@
1
1
  <script lang="ts" setup>
2
- import { defineOptions, ref } from 'vue'
2
+ import { defineOptions, defineProps, ref } from 'vue'
3
3
  import { onLoad, onShow } from '@dcloudio/uni-app'
4
4
  import { enums, listData, pageConfig, pageKey } from '../../api/page'
5
5
  import foldCard from '../fold-card/fold-card.vue'
@@ -11,6 +11,9 @@ import CardBotomButtons from '../card-botom-buttons/card-botom-buttons.vue'
11
11
  defineOptions({
12
12
  name: 'WuiList',
13
13
  })
14
+ defineProps<{
15
+ detailButtonHandle?: () => void
16
+ }>()
14
17
  const Zpaging = ref<any>(null)
15
18
  const config = ref<Config>({
16
19
  buttons: [],
@@ -129,7 +132,22 @@ async function queryList(pageNo: number, pageSize: number) {
129
132
  async function getEnums() {
130
133
  try {
131
134
  const params: string[] = []
132
- config.value.criterias?.forEach((item: any) => {
135
+ const criterias = config.value?.criterias ?? []
136
+ const columns = config.value.columns ?? []
137
+ const writes = config.value.rowActions?.reduce((acc: any, cur: any) => {
138
+ return [...acc, ...cur.writes]
139
+ }, []) || []
140
+ columns.forEach((item: any) => {
141
+ if (item.controlType === 'select' || item.controlType === 'multiselect') {
142
+ params.push(`mstrucIds=${item.mstrucId}`)
143
+ }
144
+ })
145
+ criterias.forEach((item: any) => {
146
+ if (item.controlType === 'select' || item.controlType === 'multiselect') {
147
+ params.push(`mstrucIds=${item.mstrucId}`)
148
+ }
149
+ })
150
+ writes.forEach((item: any) => {
133
151
  if (item.controlType === 'select' || item.controlType === 'multiselect') {
134
152
  params.push(`mstrucIds=${item.mstrucId}`)
135
153
  }
@@ -165,10 +183,10 @@ function submitSearch(data: any) {
165
183
  </view>
166
184
  <slot name="top" />
167
185
  </template>
168
- <foldCard v-for="item in datas" :key="item.code" :enum-column="enumColumn" :collapse-num="5" :source-id="sourceId" :groups="config" :data="item" :columns="config.columns" :primary-column="config.primaryColumn" :second-column="config.secondColumn" :label-column="config.labelColumn" model="complex">
186
+ <foldCard v-for="(item, index) in datas" :key="item.code" :index="index" :enum-column="enumColumn" :collapse-num="5" :source-id="sourceId" :groups="config" :data="item" :columns="config.columns" :primary-column="config.primaryColumn" :second-column="config.secondColumn" :label-column="config.labelColumn" model="complex">
169
187
  <template #buttons>
170
188
  <slot name="cardBotomButtons" :data="item" />
171
- <CardBotomButtons :zpaging="Zpaging" :row-actions="config.rowActions" :ractions="config.ractions" :source-id="sourceId" :item="config" :code="item.code" :page-type="pageType" :data="item" />
189
+ <CardBotomButtons :detail-button-handle="detailButtonHandle" :zpaging="Zpaging" :enum-column="enumColumn" :row-actions="config.rowActions" :ractions="config.ractions" :source-id="sourceId" :item="config" :code="item.code" :page-type="pageType" :data="item" />
172
190
  </template>
173
191
  </foldCard>
174
192
  <template #bottom>
@@ -246,7 +246,7 @@ function submitSearch(data: any) {
246
246
  <slot name="top" />
247
247
  </template>
248
248
 
249
- <foldCard v-for="item in datas" :key="item.code" :data="item" :columns="config.columns" :primary-column="config.primaryColumn" :second-column="config.secondColumn" :model="model" :groups="config" :source-id="sourceId" @click="() => { handleChange(item) }">
249
+ <foldCard v-for="(item, index) in datas" :key="item.code" :index="index" :data="item" :columns="config.columns" :primary-column="config.primaryColumn" :second-column="config.secondColumn" :model="model" :groups="config" :source-id="sourceId" @click="() => { handleChange(item) }">
250
250
  <template #select>
251
251
  <wd-checkbox :true-value="`${item.code}`" false-value="" :model-value="selectDataforMat[item.code]" />
252
252
  </template>
@@ -79,6 +79,7 @@ export function useManualTheme() {
79
79
  */
80
80
  function selectThemeColor(option: ThemeColorOption) {
81
81
  store.setCurrentThemeColor(option)
82
+ store.setNavigationBarColor()
82
83
  closeThemeColorPicker()
83
84
  }
84
85
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wui-components-v2",
3
- "version": "1.0.98",
3
+ "version": "1.1.1",
4
4
  "description": "wui 组件库",
5
5
  "author": "wgxshh",
6
6
  "license": "MIT",
@@ -60,8 +60,8 @@ export const useManualThemeStore = defineStore('manualTheme', {
60
60
  */
61
61
  setNavigationBarColor() {
62
62
  uni.setNavigationBarColor({
63
- frontColor: this.theme === 'light' ? '#000000' : '#ffffff',
64
- backgroundColor: this.theme === 'light' ? '#ffffff' : '#000000',
63
+ frontColor: this.theme === 'light' ? '#ffffff' : '#ffffff',
64
+ backgroundColor: this.theme === 'light' ? this.themeVars.colorTheme : '#000000',
65
65
  })
66
66
  },
67
67
 
package/utils/index.ts CHANGED
@@ -39,7 +39,7 @@ export function formatItemData(data: any, type: string) {
39
39
  return data ? data.split('.')[0] : ''
40
40
  }
41
41
 
42
- if (type === 'file' || type === 'relfile') {
42
+ if (type === 'file' || type === 'relfile' || type === 'video') {
43
43
  if (data) {
44
44
  // 判断是否为JSON文件
45
45
  if (typeof data === 'string') {