Skip to content

Commit fd64318

Browse files
Update SourceSelectConfigurator.vue
1 parent 5b7cb4d commit fd64318

1 file changed

Lines changed: 357 additions & 0 deletions

File tree

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

0 commit comments

Comments
 (0)