| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- <template>
- <view class="notice_header" :style="{ paddingTop: statusBarHeight + 'rpx' }">
- <!-- 顶部栏 -->
- <view class="header_top">
- <view class="header_left">
- <uni-icons type="back" size="24" color="#333333" @click="goBack"></uni-icons>
- <text class="header_title">{{ title }}</text>
- </view>
- <view v-if="showSearch" class="search_box">
- <image src="@/static/image/overview/search.png" class="search_icon" ></image>
- <input type="text" class="search_input" placeholder="请输入关键字搜索" placeholder-class="search_placeholder" v-model="searchWord" @input="handleSearchInput" />
- </view>
- <text v-else-if="showSave" class="save_btn" @click="handleSave">保存</text>
- </view>
- <!-- 通知模式过滤栏 -->
- <view v-if="showFilter && mode === 'notice'" class="filter_bar">
- <picker mode="selector" :range="typeOptions" :value="typeIndex" @change="handleTypeChange">
- <view class="filter_item">
- <text class="filter_text">{{ typeOptions[typeIndex] }}</text>
- <uni-icons type="down" size="16" color="#999999"></uni-icons>
- </view>
- </picker>
- <picker mode="selector" :range="timeOptions" :value="timeIndex" @change="handleTimeChange">
- <view class="filter_item">
- <text class="filter_text">{{ timeOptions[timeIndex] }}</text>
- <uni-icons type="down" size="16" color="#999999"></uni-icons>
- </view>
- </picker>
- </view>
- <!-- 通用过滤栏(任务总览/材料采集模式) -->
- <view v-if="showFilter && (mode === 'taskOverview' || mode === 'material')" class="filter_bar task_overview_filter">
- <picker mode="selector" class="picker_wide" :range="academicYearOptions" :value="academicYearIndex" @change="handleAcademicYearChange">
- <view class="filter_item">
- <text class="filter_text">{{ academicYearOptions[academicYearIndex] }}</text>
- <uni-icons type="down" size="16" color="#999999"></uni-icons>
- </view>
- </picker>
- <picker mode="selector" class="picker_narrow" :range="categoryOptions" :value="categoryIndex" @change="handleCategoryChange">
- <view class="filter_item">
- <text class="filter_text">{{ categoryOptions[categoryIndex] }}</text>
- <uni-icons type="down" size="16" color="#999999"></uni-icons>
- </view>
- </picker>
- <picker mode="selector" class="picker_narrow" :range="materialTypeOptions" :value="materialTypeIndex" @change="handleMaterialTypeChange">
- <view class="filter_item">
- <text class="filter_text">{{ materialTypeOptions[materialTypeIndex] }}</text>
- <uni-icons type="down" size="16" color="#999999"></uni-icons>
- </view>
- </picker>
- </view>
- <!-- 材料采集模式阶段按钮 -->
- <view v-if="showStageBtn && mode === 'material'" class="stage_btn_bar">
- <view
- class="stage_btn"
- :class="{ active: stageIndex === 0 }"
- @click="handleStageChange(0)"
- >
- <text>进行中</text>
- </view>
- <view
- class="stage_btn"
- :class="{ active: stageIndex === 1 }"
- @click="handleStageChange(1)"
- >
- <text>已结束</text>
- </view>
- </view>
- <!-- 通用标签栏(三种模式通用) -->
- <view
- v-if="(showTabs || (showCreateBtn && mode === 'taskOverview'))"
- class="tabs_bar"
- :class="{
- 'task_overview_tabs': mode === 'taskOverview',
- 'material_tabs': mode === 'material'
- }"
- >
- <text
- v-for="(tab, idx) in tabs"
- :key="idx"
- class="tab_item"
- :class="{ active: activeTabIndex === idx }"
- @click="handleTabChange(idx)"
- >{{ tab }}</text>
- <view v-if="showCreateBtn && mode === 'taskOverview'" class="create_btn" @click="handleCreate">
- <image src="@/static/image/workspace/add.png" class="add_icon" mode="aspectFit"></image>
- <text class="create_text">新建</text>
- </view>
- </view>
- </view>
- </template>
- <script setup>
- import { ref, onMounted, computed, watch } from 'vue';
- import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
- import overviewApi from '@/reqApi/overview.js';
- import { useSafeArea } from '@/common/safeArea';
- const { statusBarHeight, initSafeArea } = useSafeArea();
- const typeIndex = ref(0);
- const timeIndex = ref(0);
- const typeData = ref([{ id: '0', name: '全部类型' }]);
- const typeOptions = ref(['全部类型']);
- const searchWord = ref('');
- const academicYearIndex = ref(0);
- const categoryIndex = ref(0);
- const materialTypeIndex = ref(0);
- const categoryDataList = ref([]); // 存储分类树形数据(来自prop)
- const categoryOptions = ref(['全部分类']); // 分类选项
- const materialTypeOptions = ref(['全部类目']); // 类目选项(当前选中分类的childList)
- const stageIndex = ref(0);
- const props = defineProps({
- title: {
- type: String,
- default: ''
- },
- mode: {
- type: String,
- default: 'notice',
- validator: (value) => ['notice', 'taskOverview', 'material'].includes(value)
- },
- showSearch: {
- type: Boolean,
- default: false
- },
- showSave: {
- type: Boolean,
- default: false
- },
- showFilter: {
- type: Boolean,
- default: false
- },
- showStageBtn: {
- type: Boolean,
- default: false
- },
- timeOptions: {
- type: Array,
- default: () => ['全部时间', '最近3天', '最近7天', '最近1月', '最近3月']
- },
- academicYearOptions: {
- type: Array,
- default: () => ['']
- },
- academicYearIds: {
- type: Array,
- default: () => ['']
- },
- categoryData: {
- type: Array,
- default: () => []
- },
- showTabs: {
- type: Boolean,
- default: false
- },
- showCreateBtn: {
- type: Boolean,
- default: false
- },
- tabs: {
- type: Array,
- default: () => ['全部', '待发布', '已发布', '已关闭']
- },
- activeTab: {
- type: Number,
- default: -1
- },
- tabMode: {
- type: String,
- default: 'status',
- validator: (value) => ['status', 'index'].includes(value)
- }
- });
- const activeTabIndex = computed(() => {
- if (props.tabMode === 'index') {
- return props.activeTab;
- }
- const statusMap = [-1, 0, 1, 2];
- return statusMap.indexOf(props.activeTab);
- });
- const emit = defineEmits([
- 'save', 'tabChange', 'typeChange', 'timeChange', 'searchChange',
- 'academicYearChange', 'categoryChange', 'materialTypeChange', 'create',
- 'stageChange'
- ]);
- const fetchNoticeTypeList = async () => {
- try {
- const res = await overviewApi.noticeTypeList();
- if (res.data && Array.isArray(res.data)) {
- const types = res.data.map(item => ({
- id: item.id,
- name: item.name
- }));
- typeData.value = [{ id: '0', name: '全部类型' }, ...types];
- typeOptions.value = ['全部类型', ...types.map(item => item.name)];
- }
- } catch (error) {
- console.error('获取通知类型失败:', error);
- }
- };
- onMounted(() => {
- initSafeArea();
- if (props.mode === 'notice') {
- fetchNoticeTypeList();
- }
- });
- // 监听学年学期数据,默认选中第一条实际数据(跳过"全部")
- watch(() => props.academicYearOptions, (newVal) => {
- if (newVal && newVal.length > 0) {
- academicYearIndex.value = 0;
- }
- }, { immediate: true });
- // 监听分类数据,填充分类选项
- watch(() => props.categoryData, (newVal) => {
- if (newVal && Array.isArray(newVal)) {
- categoryDataList.value = newVal;
- // 分类选项:全部分类 + 一级分类名称
- const categoryNames = newVal.map(item => item.categoryName);
- categoryOptions.value = ['全部分类', ...categoryNames];
- // 默认选中全部分类,类目为空
- categoryIndex.value = 0;
- materialTypeOptions.value = ['全部类目'];
- }
- }, { immediate: true });
- const handleTabChange = (idx) => {
- if (props.tabMode === 'index') {
- emit('tabChange', idx);
- } else {
- const statusMap = [-1, 0, 1, 2];
- emit('tabChange', statusMap[idx]);
- }
- };
- const handleTypeChange = (e) => {
- typeIndex.value = e.detail.value;
- const selectedItem = typeData.value[typeIndex.value];
- emit('typeChange', selectedItem.id);
- };
- const handleTimeChange = (e) => {
- timeIndex.value = e.detail.value;
- emit('timeChange', timeIndex.value);
- };
- const handleAcademicYearChange = (e) => {
- academicYearIndex.value = e.detail.value;
- const selectedId = props.academicYearIds[academicYearIndex.value] || '';
- emit('academicYearChange', academicYearIndex.value, selectedId);
- };
- const handleCategoryChange = (e) => {
- categoryIndex.value = e.detail.value;
- materialTypeIndex.value = 0;
- if (categoryIndex.value === 0) {
- // 全部分类:类目默认只有"全部类目"
- materialTypeOptions.value = ['全部类目'];
- emit('categoryChange', categoryIndex.value, '');
- } else {
- // 具体分类:加载该分类下的 childList 作为类目选项(前面加"全部类目")
- const selectedCategory = categoryDataList.value[categoryIndex.value - 1];
- const childList = selectedCategory?.childList || [];
- materialTypeOptions.value = ['全部类目', ...childList.map(child => child.categoryName)];
- emit('categoryChange', categoryIndex.value, selectedCategory?.id || '');
- }
- };
- const handleMaterialTypeChange = (e) => {
- materialTypeIndex.value = e.detail.value;
- if (categoryIndex.value === 0) {
- // 全部分类时,类目只有"全部类目"
- emit('materialTypeChange', materialTypeIndex.value, '');
- } else {
- const selectedCategory = categoryDataList.value[categoryIndex.value - 1];
- const childList = selectedCategory?.childList || [];
- // index=0 是"全部类目",无对应ID
- const selectedChildId = materialTypeIndex.value === 0 ? '' : (childList[materialTypeIndex.value - 1]?.id || '');
- emit('materialTypeChange', materialTypeIndex.value, selectedChildId);
- }
- };
- const goBack = () => {
- uni.redirectTo({
- url: '/pages/workspace/index'
- });
- };
- const handleSave = () => {
- emit('save');
- };
- const handleSearchInput = () => {
- emit('searchChange', searchWord.value);
- };
- const handleCreate = () => {
- emit('create');
- };
- const handleStageChange = (idx) => {
- stageIndex.value = idx;
- emit('stageChange', idx);
- };
- </script>
- <style lang="scss" scoped>
- .notice_header {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- display: flex;
- flex-direction: column;
- background-color: #FFFFFF;
- z-index: 999;
- .header_top {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding:56rpx 24rpx 24rpx;
- .header_left {
- display: flex;
- align-items: center;
- .header_title {
- font-weight: 600;
- font-size: 44rpx;
- color: #333333;
- margin-left: 24rpx;
- }
- }
- .search_box {
- display: flex;
- align-items: center;
- background-color: #fff;
- border-radius: 8rpx;
- padding: 0rpx 24rpx;
- width: 265rpx;
- border: 2rpx solid #DCDFE6;
- height: 72rpx;
- line-height: 72rpx;
- .search_icon {
- width: 32rpx;
- height: 32rpx;
- margin-right: 8rpx;
- }
- .search_input {
- flex: 1;
- font-size: 24rpx;
- color: #333333;
- margin-left: 8rpx;
- }
- .search_placeholder {
- color: #999999;
- }
- }
- .save_btn {
- font-size: 28rpx;
- color: #2E64FA;
- font-weight: 500;
- }
- }
- .filter_bar {
- display: flex;
- gap: 32rpx;
- padding: 20rpx 24rpx;
- background-color: #FFFFFF;
- border-top: 1rpx solid #F0F0F0;
- .filter_item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 20rpx 16rpx;
- background-color: #FFFFFF;
- border-radius: 8rpx;
- min-width: 240rpx;
- border: 2rpx solid #DCDFE6;
- .filter_text {
- font-size: 28rpx;
- color: #333333;
- margin-right: 8rpx;
- }
- }
- }
- .task_overview_filter {
- gap: 16rpx;
- .picker_wide {
- flex: 1.2;
- .filter_text{
- max-width: 240rpx !important;
- }
- }
- .picker_narrow {
- flex: 1;
- }
- .filter_item {
- width: 100%;
- min-width: 0;
- padding: 16rpx 12rpx;
- box-sizing: border-box;
- .filter_text {
- font-size: 24rpx;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- max-width: 140rpx;
- margin-right: 0rpx;
- }
- }
- }
- .tabs_bar {
- display: flex;
- height: 56rpx;
- padding: 4rpx 24rpx 12rpx 24rpx;
- .tab_item {
- font-size: 32rpx;
- color: #999999;
- font-weight: 400;
- position: relative;
- margin-right: 48rpx;
- &.active {
- color: #2E64FA;
- font-weight: 500;
- &::after {
- content: '';
- position: absolute;
- bottom: 0rpx;
- left: 50%;
- transform: translateX(-50%);
- width: 100%;
- height: 4rpx;
- background-color: #2E64FA;
- }
- }
- }
- }
- .task_overview_tabs {
- justify-content: flex-start;
- align-items: center;
- height: 72rpx;
- .tab_item {
- margin-right: 48rpx;
- &.active {
- &::after {
- bottom: -8rpx;
- }
- }
- }
- .create_btn {
- margin-left: auto;
- background-color: #2E64FA;
- border-radius: 8rpx;
- width: 148rpx;
- height: 72rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 8rpx;
- .add_icon {
- width: 28rpx;
- height: 28rpx;
- }
- .create_text {
- color: #FFFFFF;
- font-size: 28rpx;
- font-weight: 500;
- }
- }
- }
- // 材料采集模式 - 阶段按钮
- .stage_btn_bar {
- display: flex;
- gap: 30rpx;
- padding:0rpx 24rpx 24rpx 24rpx;
- .stage_btn {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: center;
- height: 64rpx;
- border-radius: 8rpx;
- font-size: 28rpx;
- color: #333333;
- border: 2rpx solid #DCDFE6;
- border-radius: 8rpx 8rpx 8rpx 8rpx;
- &.active {
- background: rgba(46,100,250,0.1);
- color: #2E64FA;
- border: 2rpx solid #2E64FA;
- }
- }
- }
- // 材料采集模式 - 标签栏
- .material_tabs {
- height: 48rpx;
- padding: 4rpx 24rpx 16rpx 24rpx;
- .tab_item {
- font-size: 28rpx;
- margin-right: 48rpx;
- color: #999999;
- font-weight: 400;
- &.active {
- color: #2E64FA;
- font-weight: 500;
- }
- }
- }
- }
- </style>
|