Browse Source

feat: 添加云商城共享组件

新增商品 Emoji、支付方式选择与订单状态标签组件。

Co-authored-by: Cursor <cursoragent@cursor.com>
Pchen. 3 weeks ago
parent
commit
8532830f03

+ 48 - 0
src/components/store/GoodsEmoji.vue

@@ -0,0 +1,48 @@
+<template>
+  <span class="goods-emoji" :class="`goods-emoji--${size}`" role="img" :aria-label="ariaLabel">
+    {{ emoji }}
+  </span>
+</template>
+
+<script setup>
+import { computed } from 'vue'
+import { getGoodsEmoji } from '@/utils/storeFormat'
+
+const props = defineProps({
+  icon: { type: String, default: '' },
+  size: {
+    type: String,
+    default: 'md',
+    validator: (v) => ['sm', 'md', 'lg', 'xl'].includes(v)
+  },
+  ariaLabel: { type: String, default: '商品图标' }
+})
+
+const emoji = computed(() => getGoodsEmoji(props.icon))
+</script>
+
+<style scoped lang="less">
+.goods-emoji {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  line-height: 1;
+  user-select: none;
+
+  &--sm {
+    font-size: 2rem;
+  }
+
+  &--md {
+    font-size: 2.75rem;
+  }
+
+  &--lg {
+    font-size: 3.5rem;
+  }
+
+  &--xl {
+    font-size: 4.5rem;
+  }
+}
+</style>

+ 38 - 0
src/components/store/OrderStateTag.vue

@@ -0,0 +1,38 @@
+<template>
+  <a-tag :color="meta.color" size="small" class="order-state-tag">
+    <span class="dot" :class="`dot--${state}`" />
+    {{ meta.label }}
+  </a-tag>
+</template>
+
+<script setup>
+import { computed } from 'vue'
+import { getOrderStateMeta } from '@/utils/storeFormat'
+
+const props = defineProps({
+  state: { type: Number, required: true }
+})
+
+const meta = computed(() => getOrderStateMeta(props.state))
+const state = computed(() => props.state)
+</script>
+
+<style scoped lang="less">
+.order-state-tag {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+}
+
+.dot {
+  width: 6px;
+  height: 6px;
+  border-radius: 50%;
+  background: currentColor;
+
+  &--0 { opacity: 1; }
+  &--1 { opacity: 0.9; }
+  &--2 { opacity: 0.85; }
+  &--3 { opacity: 0.6; }
+}
+</style>

+ 74 - 0
src/components/store/PayMethodPicker.vue

@@ -0,0 +1,74 @@
+<template>
+  <div class="pay-methods">
+    <div
+      v-for="item in methods"
+      :key="item.value"
+      class="pay-method"
+      :class="{ active: modelValue === item.value }"
+      @click="$emit('update:modelValue', item.value)"
+    >
+      <component :is="item.icon" :size="28" />
+      <span>{{ item.label }}</span>
+      <icon-check-circle-fill v-if="modelValue === item.value" class="check" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import {
+  IconAlipayCircle,
+  IconWechatpay,
+  IconCheckCircleFill
+} from '@arco-design/web-vue/es/icon'
+
+defineProps({
+  modelValue: { type: String, default: '' }
+})
+
+defineEmits(['update:modelValue'])
+
+const methods = [
+  { value: 'alipay', label: '支付宝', icon: IconAlipayCircle },
+  { value: 'wxpay', label: '微信支付', icon: IconWechatpay }
+]
+</script>
+
+<style scoped lang="less">
+@import '@/styles/store-theme.less';
+
+.pay-methods {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.pay-method {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  padding: 14px 16px;
+  border: 2px solid @store-card-border;
+  border-radius: @store-radius-sm;
+  cursor: pointer;
+  transition: border-color 0.2s, background 0.2s, box-shadow 0.2s;
+  position: relative;
+  color: @store-primary;
+
+  &:hover {
+    border-color: fade(@store-accent, 50%);
+    background: fade(@store-accent, 6%);
+  }
+
+  &.active {
+    border-color: @store-accent;
+    background: fade(@store-accent, 10%);
+    box-shadow: 0 0 0 1px fade(@store-accent, 20%);
+  }
+
+  .check {
+    margin-left: auto;
+    color: @store-accent;
+    font-size: 20px;
+  }
+}
+</style>