From e220ffde8924dbfd2319012f5aef83865244bc22 Mon Sep 17 00:00:00 2001
From: xiangpei <xiangpei@timesnew.cn>
Date: 星期四, 12 六月 2025 16:10:18 +0800
Subject: [PATCH] 发布视频支持图片
---
pages/tabbar/video/video.vue | 305 +++++++++++++++++++++++++------------
pages/tabbar/index/home1.vue | 144 ++++++++++++++++++
2 files changed, 348 insertions(+), 101 deletions(-)
diff --git a/pages/tabbar/index/home1.vue b/pages/tabbar/index/home1.vue
new file mode 100644
index 0000000..1a5edcc
--- /dev/null
+++ b/pages/tabbar/index/home1.vue
@@ -0,0 +1,144 @@
+<template>
+ <!-- 鑷畾涔夋帶鍒舵潯 -->
+ <view
+ @touchstart="handleTouchStart"
+ @touchmove="handleTouchMove"
+ @touchend="handleTouchEnd"
+ class="container">
+ <!-- 杩涘害鏉� - 鏁翠釜鍖哄煙鍙嫋鍔� -->
+ <view class="process-warp" :style="{ opacity: showProcess ? 1 : 0 }">
+ <!-- 鏄剧ず褰撳墠杩涘害 -->
+ <view class="progress-text">{{ hasPlayTime }}/{{formartDuration}}</view>
+ <view
+ class="progress-bar"
+ id="progressBar"
+ >
+
+ <!-- 宸插~鍏呴儴鍒� -->
+ <view class="progress-fill" :style="{ width: progress + '%' }"></view>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script>
+export default {
+ data() {
+ return {
+ startX: 0,
+ progress: 0, // 瑙嗛杩涘害
+ startProgress : 0, // 寮�濮嬫粦鍔ㄦ椂鐨勮繘搴�
+ barLeft: 0, // 杩涘害鏉″乏杈圭晫浣嶇疆
+ barWidth: 0, // 杩涘害鏉″搴�
+ isDragging: false, // 鏄惁姝e湪鎷栧姩
+ processHidenTimer: null, // 杩涘害鏉¢殣钘忓畾鏃跺櫒
+ showProcess: false, // 鏄惁鏄剧ず杩涘害鏉�
+ };
+ },
+ mounted() {
+ // 鑾峰彇杩涘害鏉$殑灏哄鍜屼綅缃俊鎭�
+ this.getBarRect();
+ },
+ methods: {
+ // 鑾峰彇杩涘害鏉$殑浣嶇疆鍜屽昂瀵�
+ getBarRect() {
+ const query = uni.createSelectorQuery().in(this);
+ query.select('#progressBar').boundingClientRect(rect => {
+ if (rect) {
+ this.barLeft = rect.left;
+ this.barWidth = rect.width;
+ }
+ }).exec();
+ },
+
+ // 瑙︽懜寮�濮�
+ handleTouchStart(e) {
+ this.isDragging = true;
+ this.showProcess = true;
+ this.startProgress = this.progress; // 璁板綍寮�濮嬫椂鐨勮繘搴�
+ this.startX = e.touches[0].pageX;
+ console.log("璁板綍寮�濮嬫椂鐨勮繘搴�", this.startProgress);
+ this.videoContexts[this.currentIndex].pause()
+ // this.updateProgress(e);
+ },
+
+ // 瑙︽懜绉诲姩
+ handleTouchMove(e) {
+ if (!this.isDragging || !this.barWidth) return;
+ clearTimeout(this.processHidenTimer)
+ this.videoContexts[this.currentIndex].pause()
+ this.updateProgress(e);
+ },
+
+ // 瑙︽懜缁撴潫
+ handleTouchEnd() {
+ this.isDragging = false;
+ console.log("婊戝姩缁撴潫", this.duration * this.progress);
+ this.videoContexts[this.currentIndex].seek(this.duration * this.progress / 100)
+ this.videoContexts[this.currentIndex].play()
+ this.processHidenTimer = setTimeout(() => {
+ this.showProcess = false;
+ }, 1000);
+ },
+
+ // 鏇存柊杩涘害
+ updateProgress(e) {
+ // 鑾峰彇褰撳墠瑙︽懜鐐筙鍧愭爣
+ const currentX = e.touches[0].pageX;
+
+ // 璁$畻婊戝姩璺濈(鍍忕礌)
+ const deltaX = currentX - this.startX;
+
+ // 灏嗗儚绱犺窛绂昏浆鎹负杩涘害澧為噺
+ const deltaProgress = (deltaX / this.barWidth) * 100;
+ console.log("杩涘害澧為噺", deltaProgress);
+ // 璁$畻鏂拌繘搴� = 寮�濮嬫椂鐨勮繘搴� + 婊戝姩澧為噺
+ let newProgress = this.startProgress + deltaProgress;
+
+ // 闄愬埗鑼冨洿鍦�0-100涔嬮棿
+ newProgress = Math.max(0, Math.min(100, newProgress));
+
+ this.progress = newProgress;
+ }
+ }
+};
+</script>
+
+<style>
+.container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ }
+
+ .progress-bar {
+ position: relative;
+ width: 100%;
+ height: 16px;
+ background-color: #eee;
+ overflow: hidden;
+ }
+
+ .progress-fill {
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: 100%;
+ background-color: lightgray;
+ transition: width 0.1s;
+ }
+ .process-warp {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+ .progress-text {
+ margin-top: 10px;
+ font-size: 14px;
+ color: #666;
+ }
+</style>
\ No newline at end of file
diff --git a/pages/tabbar/video/video.vue b/pages/tabbar/video/video.vue
index 18d38ae..b996395 100644
--- a/pages/tabbar/video/video.vue
+++ b/pages/tabbar/video/video.vue
@@ -1,16 +1,22 @@
<template>
<view class="publish-container">
+ <u-popup v-model="fileTypeShow" mode="bottom" round="20" height="35%">
+ <view style="width: 100%;height:100%;display: flex;flex-direction: column;justify-content: center;align-items: center;">
+ <view>璇烽�夋嫨瑕佸彂甯冪殑绫诲瀷</view>
+ <u-button style="width: 50%;margin-bottom: 30rpx;margin-top: 20rpx;" type="success" @click="chooseVideo">瑙嗛</u-button>
+ <u-button style="width: 50%;" type="success" @click="chooseImgs">鍥剧墖</u-button>
+ </view>
+ </u-popup>
<!-- 瑙嗛涓婁紶鍖哄煙 -->
<view class="upload-section">
- <view class="upload-btn" @click="chooseVideo" v-if="!videoInfo.url">
+ <view class="upload-btn" @click="this.fileTypeShow = true" v-if="!formData.videoFileKey && formData.videoImgs.length < 1">
<u-icon name="plus" size="40" color="#999"></u-icon>
- <text class="upload-text">鐐瑰嚮涓婁紶瑙嗛</text>
- <text class="upload-tips">鏀寔MP4鏍煎紡锛屾渶闀�60绉�</text>
+ <text class="upload-text">鐐瑰嚮涓婁紶</text>
</view>
-
- <view class="video-preview" v-else>
- <video
- :src="videoInfo.url"
+
+ <view class="video-preview" v-else-if="formData.videoContentType === 'video'">
+ <video
+ :src="videoInfo.url"
:object-fit="formData.videoFit"
class="video-player"
:poster="videoInfo.cover || ''"
@@ -20,30 +26,48 @@
</view>
<view class="video-actions">
<u-button type="error" size="mini" @click="reUpload">閲嶆柊涓婁紶</u-button>
- <u-button type="primary" size="mini" @click="chooseCover" v-if="videoInfo.url">{{formData.cover ? '鏇存崲灏侀潰' : '璇烽�夋嫨灏侀潰'}}</u-button>
+ <u-button type="primary" size="mini" @click="chooseCover" v-if="formData.videoFileKey">{{formData.cover ? '鏇存崲灏侀潰' : '璇烽�夋嫨灏侀潰'}}</u-button>
</view>
</view>
+
+ <view class="image-list" v-else-if="formData.videoContentType === 'img'">
+ <view
+ v-for="item in videoPreviewImgs"
+ :key="item"
+ class="image-item"
+ :style="{width: itemWidth + 'px', height: itemWidth + 'px'}"
+ >
+ <image
+ :src="item"
+ mode="aspectFill"
+ class="image"
+ />
+ </view>
+ <view class="video-actions">
+ <u-button type="error" size="mini" @click="reUpload">閲嶆柊涓婁紶</u-button>
+ </view>
+ </view>
</view>
-
+
<!-- 瑙嗛淇℃伅琛ㄥ崟 -->
<view class="form-section">
<u-form :model="formData" ref="formRef" labelWidth="80">
<!-- 鏍囬杈撳叆 -->
<u-form-item label="鏍囬" prop="title" borderBottom>
- <u-input
- v-model="formData.title"
+ <u-input
+ v-model="formData.title"
placeholder="璇疯緭鍏ヨ棰戞爣棰�,20瀛椾互鍐�"
maxlength="20"
show-word-limit
clearable
/>
</u-form-item>
-
+
<!-- 璇濋杈撳叆 -->
<u-form-item label="璇濋" prop="tags" borderBottom>
<view class="tags-input-container">
- <u-input
- v-model="tagInput"
+ <u-input
+ v-model="tagInput"
placeholder="杈撳叆璇濋锛屽洖杞︾‘璁�"
clearable
@confirm="addTag"
@@ -52,7 +76,7 @@
></u-input>
<!-- 宸查�夎瘽棰樺睍绀� -->
<view class="tags-display" v-if="formData.tags.length > 0">
- <my-tag
+ <my-tag
v-for="(tag, index) in formData.tags"
:key="index"
:text="tag.tagName"
@@ -69,7 +93,7 @@
<view class="hot-topics" v-if="showTopicRecommendations">
<text class="section-title">{{ tagInput ? '鎺ㄨ崘璇濋' : '鐑棬璇濋' }}</text>
<view class="topic-list">
- <my-tag
+ <my-tag
v-for="(tag, index) in recommendedTags"
:key="index"
:text="tag.tagName"
@@ -81,22 +105,22 @@
</view>
</view>
</u-form-item>
-
-
+
+
<!-- 鍟嗗搧閾炬帴 -->
<u-form-item label="鍟嗗搧" prop="goodsId" borderBottom>
<view class="goods-link-container">
- <u-input
+ <u-input
placeholder="鍙�夋嫨鎺ㄨ崘鍟嗗搧"
clearable
v-if="!selectedGoods"
@click="chooseGoods"
disabled
>
- <u-icon
- slot="right"
- name="search"
- size="24"
+ <u-icon
+ slot="right"
+ name="search"
+ size="24"
@click="chooseGoods"
></u-icon>
</u-input>
@@ -106,9 +130,9 @@
<text class="goods-name">{{ selectedGoods.name }}</text>
<text class="goods-price">楼{{ selectedGoods.price }}</text>
</view>
- <u-icon
- name="close"
- size="20"
+ <u-icon
+ name="close"
+ size="20"
@click="clearGoods"
></u-icon>
</view>
@@ -116,12 +140,12 @@
</u-form-item>
</u-form>
</view>
-
+
<!-- 鍙戝竷鎸夐挳 -->
<view class="publish-btn">
- <u-button
- type="primary"
- shape="circle"
+ <u-button
+ type="success"
+ shape="circle"
:loading="loading"
@click="handlePublish"
:disabled="!canPublish"
@@ -129,7 +153,7 @@
{{ loading ? '鍙戝竷涓�...' : '绔嬪嵆鍙戝竷' }}
</u-button>
</view>
-
+
<!-- 鍟嗗搧閫夋嫨寮圭獥 -->
<u-popup v-model="showGoodsPicker" mode="bottom" round="20" height="70%">
<view class="goods-picker">
@@ -138,16 +162,16 @@
<u-icon name="close" size="24" @click="showGoodsPicker = false"></u-icon>
</view>
<view class="search-bar">
- <u-search
- v-model="goodsSearch"
- placeholder="鎼滅储鍟嗗搧鍚嶇О"
+ <u-search
+ v-model="goodsSearch"
+ placeholder="鎼滅储鍟嗗搧鍚嶇О"
:showAction="false"
></u-search>
</view>
<scroll-view class="goods-list" scroll-y>
- <view
- class="goods-item"
- v-for="goods in filteredGoods"
+ <view
+ class="goods-item"
+ v-for="goods in filteredGoods"
:key="goods.id"
@click="selectGoods(goods)"
>
@@ -156,16 +180,16 @@
<text class="goods-name">{{ goods.name }}</text>
<text class="goods-price">楼{{ goods.price }}</text>
</view>
- <u-icon
- name="checkmark"
- size="24"
+ <u-icon
+ name="checkmark"
+ size="24"
:color="selectedGoods && selectedGoods.id === goods.id ? '#2979ff' : '#ccc'"
></u-icon>
</view>
</scroll-view>
</view>
</u-popup>
-
+
<custom-tabbar bgColor="#ffffff" selected="video"></custom-tabbar>
</view>
</template>
@@ -188,14 +212,17 @@
components: {MyTag,UIcon,UButton,UForm,UFormItem,UInput,USearch,UPopup},
data() {
return {
+ fileTypeShow: false,
cosClient: null,
bucket: '',
region: '',
+ endpoint: '',
videoUploadProgress: 0,
loading: false,
showGoodsPicker: false,
goodsSearch: '',
tagInput: '',
+ videoPreviewImgs: [], // 棰勮鍥剧墖鍦板潃
videoInfo: {
url: '',
fileKey: '',
@@ -212,6 +239,8 @@
videoDuration: 0,
videoFit: 'cover',
goodsId: '',
+ videoContentType: 'video',
+ videoImgs: [],
tags: [],
fileInfo: {}
},
@@ -236,25 +265,37 @@
{ required: true, message: '璇疯緭鍏ヨ棰戞爣棰�', trigger: 'blur' },
{ min: 1, max: 20, message: '鏍囬闀垮害鍦�1鍒�20涓瓧绗�', trigger: 'blur' }
]
- }
+ },
+ screenWidth: 375,
+ gap: 10 // 鍥剧墖闂磋窛
};
},
computed: {
canPublish() {
- return this.formData.videoFileKey && this.formData.title && this.formData.cover;
+ if(this.formData.videoContentType === 'video') {
+ return this.formData.videoFileKey && this.formData.title && this.formData.cover;
+ } else if(this.formData.videoContentType === 'img') {
+ return this.formData.videoImgs.length > 0 && this.formData.title;
+ }
},
filteredGoods() {
if (!this.goodsSearch) return this.goodsList;
- return this.goodsList.filter(goods =>
+ return this.goodsList.filter(goods =>
goods.name.toLowerCase().includes(this.goodsSearch.toLowerCase())
);
},
showTopicRecommendations() {
return (this.tagInput === '' || this.recommendedTags.length > 0) && this.formData.tags.length < 5;
- }
+ },
+ // 璁$畻姣忎釜鍥剧墖椤圭殑瀹藉害锛堣�冭檻闂磋窛锛�
+ itemWidth() {
+ return (this.screenWidth - (this.gap * 4) - 20) / 3
+ }
},
- created() {
-
+ onLoad() {
+ // 鑾峰彇灞忓箷瀹藉害
+ const systemInfo = uni.getSystemInfoSync()
+ this.screenWidth = systemInfo.windowWidth
},
onShow() {
this.initCOS()
@@ -278,7 +319,7 @@
getSTSToken().then(res => {
const COS = require('@/lib/cos-wx-sdk-v5.js'); // 寮�鍙戞椂浣跨敤
// const COS = require('./lib/cos-wx-sdk-v5.min.js'); // 涓婄嚎鏃朵娇鐢ㄥ帇缂╁寘
-
+
// console.log(COS.version); sdk 鐗堟湰闇�瑕佷笉浣庝簬 1.7.2
this.cosClient = new COS({
SecretId: res.data.data.tmpSecretId, // sts 鏈嶅姟涓嬪彂鐨勪复鏃� secretId
@@ -290,11 +331,17 @@
});
this.bucket = res.data.data.bucket
this.region = res.data.data.region
+ this.endpoint = res.data.data.endpoint
})
-
+
},
// 閫夋嫨瑙嗛
chooseVideo() {
+ this.fileTypeShow = false;
+ // 娓呯┖閫夋嫨鐨勫浘鐗�
+ this.videoPreviewImgs = [];
+ this.formData.videoImgs = [];
+ this.formData.videoContentType = 'video'
uni.chooseVideo({
sourceType: ['album', 'camera'],
maxDuration: 60,
@@ -304,7 +351,7 @@
// 鑾峰彇鏂囦欢鍚�
const tempPath = res.tempFilePath;
let fileName = tempPath.substring(tempPath.lastIndexOf('/') + 1);
-
+
// 澶勭悊瀹夊崜鍙兘鐨刄RI缂栫爜
if(fileName.indexOf('%') > -1) {
fileName = decodeURIComponent(fileName);
@@ -322,12 +369,12 @@
this.formData.videoDuration = res.duration;
// 鍒ゆ柇瑙嗛鐨勫~鍏呮ā寮�
this.formData.videoFit = this.calculateVideoFit(res.width, res.height)
-
+
this.cosClient.uploadFile({
Bucket: this.bucket,
Region: this.region,
Key: fileKey,
- FilePath: res.tempFilePath,
+ FilePath: res.tempFilePath,
SliceSize: 1024 * 1024 * 5, /* 瑙﹀彂鍒嗗潡涓婁紶鐨勯槇鍊�,5M */
onProgress: (progressData) => {
console.log(progressData.percent);
@@ -362,27 +409,60 @@
calculateVideoFit(width, height) {
const viewportRatio = uni.getSystemInfoSync().windowWidth / uni.getSystemInfoSync().windowHeight;
const videoRatio = width / height;
-
+
// 瑙勫垯1锛氳秴瀹借棰戯紙濡傜數褰�21:9锛�
if (videoRatio > 2) return 'contain';
-
+
// 瑙勫垯2锛氱珫灞忚棰戯紙濡�9:16锛�
if (videoRatio < 0.8) return 'cover';
-
+
// 瑙勫垯3锛氭帴杩戝睆骞曟瘮渚嬬殑妯睆瑙嗛
return Math.abs(videoRatio - viewportRatio) > 0.3 ? 'contain' : 'cover';
},
// 閲嶆柊涓婁紶
reUpload() {
- this.videoInfo = {
- url: '',
- cover: '',
- duration: 0,
- size: 0
- };
- this.chooseVideo();
+ this.resetData()
+ this.fileTypeShow = true
},
-
+ // 閫夋嫨瑙嗛鍥鹃泦
+ chooseImgs() {
+ this.fileTypeShow = false
+ // 娓呯┖閫夋嫨鐨勮棰�
+ this.formData.videoFileKey = '';
+ this.formData.cover = '';
+ this.formData.videoContentType = 'img'
+ uni.chooseImage({
+ count: 9,
+ sizeType: ['compressed'],
+ sourceType: ['album'],
+ success: (res) => {
+ res.tempFilePaths.forEach(tmpImg => {
+ let fileName = tmpImg.substring(tmpImg.lastIndexOf('/') + 1);
+ // 澶勭悊瀹夊崜鍙兘鐨刄RI缂栫爜
+ if(fileName.indexOf('%') > -1) {
+ fileName = decodeURIComponent(fileName);
+ }
+ const fileKey = getFileKey(fileName);
+ this.cosClient.uploadFile({
+ Bucket: this.bucket,
+ Region: this.region,
+ Key: fileKey,
+ FilePath: tmpImg,
+ SliceSize: 1024 * 1024 * 5 /* 瑙﹀彂鍒嗗潡涓婁紶鐨勯槇鍊�,5M */
+ }, (err, data) => {
+ if (err) {
+ console.log('涓婁紶澶辫触', err);
+ } else {
+ // 鑾峰彇灏侀潰鐨勮闂湴鍧�
+ this.videoPreviewImgs.push(this.endpoint + '/' + fileKey);
+ this.formData.videoImgs.push(fileKey);
+ }
+ });
+ })
+
+ }
+ });
+ },
// 閫夋嫨灏侀潰
chooseCover() {
uni.chooseImage({
@@ -401,41 +481,38 @@
Bucket: this.bucket,
Region: this.region,
Key: fileKey,
- FilePath: res.tempFilePaths[0],
+ FilePath: res.tempFilePaths[0],
SliceSize: 1024 * 1024 * 5 /* 瑙﹀彂鍒嗗潡涓婁紶鐨勯槇鍊�,5M */
}, (err, data) => {
if (err) {
console.log('涓婁紶澶辫触', err);
} else {
- // 鑾峰彇灏侀潰鐨勮闂湴鍧�
- getFilePreviewUrl(fileKey).then(res => {
- this.videoInfo.cover = res.data.data
- this.formData.cover = fileKey
- })
+ this.videoInfo.cover = this.endpoint + '/' + fileKey
+ this.formData.cover = fileKey
}
});
}
});
},
-
+
// 閫夋嫨鍟嗗搧
chooseGoods() {
this.showGoodsPicker = true;
},
-
+
// 閫夋嫨鍏蜂綋鍟嗗搧
selectGoods(goods) {
this.selectedGoods = goods;
this.formData.goodsId = goods.id;
this.showGoodsPicker = false;
},
-
+
// 娓呴櫎鍟嗗搧
clearGoods() {
this.selectedGoods = null;
this.formData.goodsId = '';
},
-
+
// 鎼滅储鐑棬璇濋
searchTags() {
if (this.tagInput.trim() !== '') {
@@ -466,7 +543,7 @@
});
}
},
-
+
// 閫夋嫨鎺ㄨ崘璇濋
selectTopic(index) {
const tag = this.recommendedTags[index]
@@ -477,7 +554,7 @@
});
return;
}
-
+
if (this.formData.tags.filter(item => item.tagName === tag.tagName).length < 1) {
this.formData.tags.push(tag);
this.tagInput = '';
@@ -488,12 +565,12 @@
});
}
},
-
+
// 绉婚櫎鏍囩
removeTag(index) {
this.formData.tags.splice(index, 1);
},
-
+
// 澶勭悊鍙戝竷
handlePublish() {
this.$refs.formRef.validate(valid => {
@@ -508,29 +585,11 @@
});
this.loading = false
// 閲嶇疆琛ㄥ崟
- this.videoInfo = {
- url: '',
- fileKey: '',
- fileType: '',
- fileSize: 0,
- originalFileName: '',
- cover: ''
- };
- this.formData = {
- id: '',
- title: '',
- videoFileKey: '',
- cover: '',
- videoFit: 'cover',
- videoDuration: 0,
- goodsId: '',
- tags: [],
- fileInfo: {}
- };
+ this.resetData();
this.selectedGoods = null;
this.tagInput = '';
this.recommendedTags = [];
-
+
// TODO 鍏堣烦棣栭〉,鍚庨潰璺虫垜鐨勮棰戦〉闈�
setTimeout(() => {
uni.switchTab({
@@ -545,21 +604,45 @@
});
}
});
- }
+ },
+ resetData() {
+ // 閲嶇疆琛ㄥ崟
+ this.videoInfo = {
+ url: '',
+ fileKey: '',
+ fileType: '',
+ fileSize: 0,
+ originalFileName: '',
+ cover: ''
+ };
+ this.formData = {
+ id: '',
+ title: '',
+ videoFileKey: '',
+ cover: '',
+ videoFit: 'cover',
+ videoDuration: 0,
+ goodsId: '',
+ videoContentType: 'video',
+ videoImgs: [],
+ tags: [],
+ fileInfo: {}
+ };
+ this.videoPreviewImgs = []
+ }
}
};
</script>
<style scoped>
.publish-container {
- padding: 20rpx;
+ padding: 10px;
padding-bottom: 120rpx;
}
.upload-section {
background-color: #f8f8f8;
border-radius: 16rpx;
- padding: 40rpx;
margin-bottom: 30rpx;
display: flex;
justify-content: center;
@@ -599,9 +682,11 @@
}
.video-actions {
+ width: 100%;
margin-top: 20rpx;
display: flex;
justify-content: center;
+ align-items: center;
gap: 20rpx;
}
@@ -767,4 +852,22 @@
height: 25px;
margin-top: 10px;
}
-</style>
\ No newline at end of file
+
+.image-list {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-start;
+}
+
+.image-item {
+ margin: 5px;
+ overflow: hidden;
+ border-radius: 8px;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+}
+
+.image {
+ width: 100%;
+ height: 100%;
+}
+</style>
--
Gitblit v1.8.0