Skip to content

Commit 18f0395

Browse files
Update SourceSelectConfigurator.vue
1 parent 5f09665 commit 18f0395

1 file changed

Lines changed: 344 additions & 0 deletions

File tree

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
<template>
2+
<div class="source-container">
3+
<tiny-popover
4+
placement="bottom-start"
5+
trigger="manual"
6+
v-model="isShow"
7+
:visible-arrow="false"
8+
:popper-class="['option-popper', 'fixed-left']"
9+
>
10+
<div class="source-wrap">
11+
<div class="source-title">
12+
<span>资源</span>
13+
<div class="right">
14+
<tiny-button type="primary" plain @click="setSource"> 确认 </tiny-button>
15+
<tiny-icon-close class="tiny-svg-size" @click="closePopover"></tiny-icon-close>
16+
</div>
17+
</div>
18+
<div class="source-content">
19+
<div class="source-filter">
20+
<tiny-select v-model="sourceCategory" :options="categoryOptions" placeholder="请选择分组" @change="categoryChange"> </tiny-select>
21+
<tiny-search
22+
class="search"
23+
v-model="searchWords"
24+
placeholder="按资源名称搜索"
25+
clearable
26+
>
27+
<template #prefix>
28+
<tiny-icon-search />
29+
</template>
30+
</tiny-search>
31+
</div>
32+
<div>
33+
<span
34+
:class="['source-tag', { 'source-type-active': sourceType === 'all' }]"
35+
@click="sourceTypeChange('all')"
36+
>全部</span
37+
>
38+
<tiny-divider direction="vertical"></tiny-divider>
39+
<span
40+
:class="['source-tag', { 'source-type-active': sourceType === 'image' }]"
41+
@click="sourceTypeChange('image')"
42+
>图片</span
43+
>
44+
</div>
45+
<div class="source-list">
46+
<div v-for="(item, index) in sourceList" :key="item.id" class="source-item">
47+
<img :src="item.thumbnailUrl ?? item.resourceUrl" />
48+
<tiny-radio v-model="selectedSourceIndex" @change="selectSource(index)" :label="index" text=""></tiny-radio>
49+
<div class="source-name-wrap">
50+
<span :title="item.name">{{ item.name }}</span>
51+
</div>
52+
</div>
53+
<search-empty :isShow="!sourceList?.length" />
54+
</div>
55+
</div>
56+
</div>
57+
<template #reference>
58+
<div v-if="imgSrc" class="source-img-wrap">
59+
<img :src="imgSrc" />
60+
<div class="source-img-modal">
61+
<div @click="getResourceData">选择资源</div>
62+
</div>
63+
</div>
64+
<tiny-button v-else @click="getResourceData">选择资源</tiny-button>
65+
<tiny-input v-model="imgSrc"></tiny-input>
66+
</template>
67+
</tiny-popover>
68+
</div>
69+
</template>
70+
<script>
71+
import { ref, reactive, watch } from 'vue'
72+
import { Popover, Button, Input, Search, Select, Divider, RadioGroup, Radio } from '@opentiny/vue'
73+
import { iconClose, iconSearch } from '@opentiny/vue-icon'
74+
import { SearchEmpty } from '@opentiny/tiny-engine-common'
75+
import { useNotify } from '@opentiny/tiny-engine-meta-register'
76+
import { fetchResourceListByGroupId, fetchResourceGroupByAppId } from './http'
77+
78+
const isShow = ref(false)
79+
80+
const openPopover = () => {
81+
isShow.value = true
82+
}
83+
84+
const closePopover = () => {
85+
isShow.value = false
86+
}
87+
88+
export default {
89+
components: {
90+
TinyPopover: Popover,
91+
TinyButton: Button,
92+
TinyInput: Input,
93+
TinySearch: Search,
94+
TinySelect: Select,
95+
TinyDivider: Divider,
96+
TinyRadio: Radio,
97+
TinyRadioGroup: RadioGroup,
98+
SearchEmpty,
99+
TinyIconClose: iconClose(),
100+
TinyIconSearch: iconSearch()
101+
},
102+
props: {
103+
modelValue: {
104+
type: String,
105+
default: ''
106+
}
107+
},
108+
emits: ['update:modelValue'],
109+
setup(props, { emit }) {
110+
const imgSrc = ref(props.modelValue)
111+
112+
const categoryData = ref([])
113+
114+
const categoryOptions = ref([])
115+
116+
const sourceCategory = ref()
117+
118+
const sourceType = ref('all')
119+
120+
const searchWords = ref('')
121+
122+
const sourceOriginList = ref([])
123+
124+
const sourceList = ref([])
125+
126+
const selectedSourceIndex = ref()
127+
128+
const getResourceData = () => {
129+
fetchResourceGroupByAppId().then((res) => {
130+
categoryData.value = res
131+
categoryOptions.value = res.map((item) => ({ label: item.name, value: item.id, resources: item.resources }))
132+
if (categoryOptions.value.length) {
133+
sourceCategory.value = categoryOptions.value[0].value
134+
sourceOriginList.value = categoryOptions.value[0].resources
135+
sourceList.value = categoryOptions.value[0].resources
136+
openPopover()
137+
}
138+
}).catch(error => {
139+
useNotify({
140+
type: 'error',
141+
message: error
142+
})
143+
})
144+
}
145+
146+
const categoryChange = () => {
147+
sourceOriginList.value = categoryData.value.find(item => item.id === sourceCategory.value).resources
148+
sourceList.value = sourceOriginList.value.filter(item => item.name.includes(searchWords.value))
149+
}
150+
151+
const selectSource = (sourceIndex) => {
152+
selectedSourceIndex.value = sourceIndex
153+
}
154+
155+
const sourceTypeChange = (type) => {
156+
sourceType.value = type
157+
}
158+
159+
const setSource = () => {
160+
emit('update:modelValue', sourceList.value[selectedSourceIndex.value].resourceUrl)
161+
closePopover()
162+
}
163+
164+
watch(
165+
() => searchWords.value,
166+
() => {
167+
sourceList.value = sourceOriginList.value.filter(item => item.name.includes(searchWords.value))
168+
}
169+
)
170+
171+
return {
172+
isShow,
173+
imgSrc,
174+
sourceType,
175+
searchWords,
176+
sourceList,
177+
selectedSourceIndex,
178+
categoryOptions,
179+
sourceCategory,
180+
openPopover,
181+
closePopover,
182+
getResourceData,
183+
categoryChange,
184+
selectSource,
185+
sourceTypeChange,
186+
setSource
187+
}
188+
}
189+
}
190+
</script>
191+
<style lang="less" scoped>
192+
.source-container {
193+
width: 100%;
194+
line-height: 28px;
195+
background-color: var(--ti-lowcode-input-bg);
196+
.source-img-wrap {
197+
position: relative;
198+
width: 150px;
199+
height: 96px;
200+
img {
201+
width: 100%;
202+
height: 100%;
203+
object-fit: contain;
204+
}
205+
.source-img-modal {
206+
width: 0;
207+
height: 0;
208+
overflow: hidden;
209+
position: absolute;
210+
border-radius: 2px;
211+
left: 0;
212+
top: 0;
213+
background-color: rgba(0, 0, 0, 0.2);
214+
display: flex;
215+
align-items: center;
216+
justify-content: center;
217+
218+
div {
219+
width: 70px;
220+
height: 22px;
221+
line-height: 22px;
222+
border: 1px solid #fff;
223+
border-radius: 4px;
224+
text-align: center;
225+
color: #fff;
226+
cursor: pointer;
227+
}
228+
}
229+
&:hover {
230+
.source-img-modal {
231+
width: 100%;
232+
height: 100%;
233+
}
234+
}
235+
}
236+
}
237+
238+
.source-wrap {
239+
width: 392px;
240+
border-radius: 4px;
241+
242+
.source-title {
243+
display: flex;
244+
justify-content: space-between;
245+
align-items: center;
246+
margin: 8px 0 20px 0;
247+
248+
.right {
249+
svg {
250+
margin-left: 10px;
251+
}
252+
}
253+
254+
span {
255+
font-weight: 600;
256+
}
257+
258+
.tiny-svg {
259+
cursor: pointer;
260+
}
261+
}
262+
263+
.source-content {
264+
display: flex;
265+
flex-direction: column;
266+
gap: 12px;
267+
.source-filter {
268+
display: flex;
269+
:deep(.tiny-select .tiny-input__inner) {
270+
border-radius: 4px 0 0 4px;
271+
}
272+
:deep(.tiny-search .tiny-search__line) {
273+
border-left: none;
274+
border-radius: 0 4px 4px 0;
275+
}
276+
}
277+
.source-tag {
278+
cursor: pointer;
279+
}
280+
.source-type-active {
281+
font-weight: 600;
282+
}
283+
.source-list {
284+
display: flex;
285+
flex-wrap: wrap;
286+
gap: 16px;
287+
288+
.empty-wrap {
289+
width: 100%
290+
}
291+
292+
.source-item {
293+
width: 120px;
294+
height: 78px;
295+
position: relative;
296+
background-color: #f5f5f5;
297+
border-radius: 2px;
298+
299+
.tiny-radio {
300+
position: absolute;
301+
left: 8px;
302+
top: 8px;
303+
:deep(.tiny-radio__label) {
304+
visibility: hidden;
305+
}
306+
}
307+
308+
img {
309+
width: 100%;
310+
height: 100%;
311+
object-fit: contain;
312+
}
313+
314+
.source-name-wrap {
315+
display: none;
316+
height: 28px;
317+
position: absolute;
318+
background: linear-gradient(to top, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0));
319+
left: 0;
320+
right: 0;
321+
bottom: 0;
322+
text-align: center;
323+
line-height: 28px;
324+
width: 120px;
325+
color: #fff;
326+
white-space: nowrap;
327+
overflow: hidden;
328+
text-overflow: ellipsis;
329+
330+
span {
331+
padding: 0 6px;
332+
}
333+
}
334+
335+
&:hover {
336+
.source-name-wrap {
337+
display: block;
338+
}
339+
}
340+
}
341+
}
342+
}
343+
}
344+
</style>

0 commit comments

Comments
 (0)